QAway-to commited on
Commit
5aa1b59
·
1 Parent(s): 286a523

New tabs and functions v1.3

Browse files
Files changed (2) hide show
  1. core/crypto_dashboard.py +106 -0
  2. core/visual_metrics.py +0 -81
core/crypto_dashboard.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 🇬🇧 Module: crypto_dashboard.py
3
+ Purpose: Build Power BI–style dashboard for crypto market using CoinGecko API + portfolio metrics.
4
+ 🇷🇺 Модуль: crypto_dashboard.py
5
+ Назначение: Создание Power BI-подобного дашборда на основе CoinGecko API и метрик портфеля.
6
+ """
7
+
8
+ import requests
9
+ import pandas as pd
10
+ import plotly.graph_objects as go
11
+ import plotly.express as px
12
+ from datetime import datetime
13
+ from services.output_api import fetch_metrics_async
14
+ from services.llm_client import llm_service
15
+ import asyncio
16
+
17
+ COINGECKO_API = "https://api.coingecko.com/api/v3"
18
+
19
+
20
+ async def _get_portfolio_metrics(portfolio_id):
21
+ metrics = await fetch_metrics_async(portfolio_id)
22
+ return metrics or {}
23
+
24
+
25
+ def get_market_data(coin_id: str = "bitcoin", days: int = 30):
26
+ """Fetch market data (price, volume, market cap) from CoinGecko."""
27
+ url = f"{COINGECKO_API}/coins/{coin_id}/market_chart?vs_currency=usd&days={days}"
28
+ r = requests.get(url)
29
+ data = r.json()
30
+ df = pd.DataFrame(data["prices"], columns=["timestamp", "price"])
31
+ df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
32
+ df["returns"] = df["price"].pct_change()
33
+ df["volatility"] = df["returns"].rolling(7).std() * 100
34
+ return df
35
+
36
+
37
+ def build_dashboard(coin_id: str, portfolio_id: str, days: int = 30):
38
+ """Main sync wrapper: returns 3 Plotly figures + LLM commentary."""
39
+ df = get_market_data(coin_id, days)
40
+ metrics = asyncio.run(_get_portfolio_metrics(portfolio_id))
41
+
42
+ if df.empty:
43
+ return None, None, None, "❌ No market data."
44
+
45
+ # === KPI Cards ===
46
+ growth = (df["price"].iloc[-1] / df["price"].iloc[0] - 1) * 100
47
+ vol = df["volatility"].mean()
48
+ sharpe = metrics.get("sharpe", 0)
49
+
50
+ kpi = go.Figure()
51
+ kpi.add_trace(go.Indicator(
52
+ mode="number+delta",
53
+ value=round(growth, 2),
54
+ delta={"reference": 0, "position": "right"},
55
+ title={"text": f"{coin_id.capitalize()} Growth %"},
56
+ domain={'row': 0, 'column': 0}
57
+ ))
58
+ kpi.add_trace(go.Indicator(
59
+ mode="number",
60
+ value=round(vol, 2),
61
+ title={"text": "Volatility %"},
62
+ domain={'row': 0, 'column': 1}
63
+ ))
64
+ kpi.add_trace(go.Indicator(
65
+ mode="number",
66
+ value=round(sharpe, 2),
67
+ title={"text": "Sharpe (Portfolio)"},
68
+ domain={'row': 0, 'column': 2}
69
+ ))
70
+ kpi.update_layout(grid={'rows': 1, 'columns': 3}, template="plotly_dark", height=200)
71
+
72
+ # === Price timeline ===
73
+ price_fig = px.line(df, x="timestamp", y="price", title=f"{coin_id.capitalize()} Price (USD)")
74
+ price_fig.update_layout(template="plotly_dark", height=400)
75
+
76
+ # === Volatility chart ===
77
+ vol_fig = px.area(df, x="timestamp", y="volatility", title="7-Day Rolling Volatility")
78
+ vol_fig.update_traces(line_color="#4f46e5")
79
+ vol_fig.update_layout(template="plotly_dark", height=300)
80
+
81
+ # === Generate AI summary ===
82
+ commentary = _generate_ai_comment(df, coin_id, metrics)
83
+ return kpi, price_fig, vol_fig, commentary
84
+
85
+
86
+ def _generate_ai_comment(df, coin_id, metrics):
87
+ """Generate an AI summary (LLM commentary)."""
88
+ change = (df["price"].iloc[-1] / df["price"].iloc[0] - 1) * 100
89
+ avg_vol = df["volatility"].mean()
90
+ prompt = f"""
91
+ Act as a financial analyst.
92
+ Summarize {coin_id.capitalize()} market performance over the last period.
93
+
94
+ - Growth: {change:.2f}%
95
+ - Average Volatility: {avg_vol:.2f}%
96
+ - Portfolio Sharpe: {metrics.get('sharpe', 0):.2f}
97
+
98
+ Provide 3–5 concise sentences: performance, stability, and potential outlook.
99
+ """
100
+ commentary = ""
101
+ for delta in llm_service.stream_chat(
102
+ messages=[{"role": "user", "content": prompt}],
103
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct"
104
+ ):
105
+ commentary += delta
106
+ return commentary
core/visual_metrics.py DELETED
@@ -1,81 +0,0 @@
1
- """
2
- 🇬🇧 Module: visual_metrics.py!
3
- Purpose: DAX-style KPI calculations and interactive charts for dashboards.
4
- 🇷🇺 Модуль: visual_metrics.py
5
- Назначение: DAX-подобные вычисления KPI и построение интерактивных графиков.
6
- """
7
-
8
- import pandas as pd
9
- import plotly.express as px
10
- import plotly.graph_objects as go
11
- from services.output_api import fetch_metrics_async, fetch_absolute_pnl_async
12
- import asyncio
13
-
14
-
15
- async def _load_full_data(portfolio_id: str):
16
- metrics = await fetch_metrics_async(portfolio_id)
17
- pnl = await fetch_absolute_pnl_async(portfolio_id)
18
- df = pd.DataFrame(pnl)
19
- if "value" in df:
20
- df["value"] = pd.to_numeric(df["value"], errors="coerce")
21
- return metrics, df
22
-
23
-
24
- def dax_return_growth(df: pd.DataFrame) -> float:
25
- """📈 Аналог DAX: CALCULATE( SUM(Profit) / EARLIER(TotalProfit) )"""
26
- if df.empty or "value" not in df:
27
- return 0
28
- return (df["value"].iloc[-1] - df["value"].iloc[0]) / abs(df["value"].iloc[0]) * 100
29
-
30
-
31
- def dax_volatility(df: pd.DataFrame) -> float:
32
- """📊 Аналог DAX: STDEVX.P(VALUES(Returns))"""
33
- if df.empty or "value" not in df:
34
- return 0
35
- return df["value"].pct_change().std() * 100
36
-
37
-
38
- async def build_dashboard_figures(portfolio_id: str):
39
- """Возвращает набор графиков и KPI, имитирующих Power BI Dashboard."""
40
- metrics, df = await _load_full_data(portfolio_id)
41
-
42
- if df.empty:
43
- return None, None, None
44
-
45
- kpi_return = dax_return_growth(df)
46
- kpi_vol = dax_volatility(df)
47
- sharpe = metrics.get("sharpe", 0)
48
-
49
- # === KPI Cards ===
50
- kpi_cards = go.Figure()
51
- kpi_cards.add_trace(go.Indicator(
52
- mode="number+delta",
53
- value=round(kpi_return, 2),
54
- delta={"reference": 0, "position": "right"},
55
- title={"text": "Portfolio Growth %"},
56
- domain={'row': 0, 'column': 0}
57
- ))
58
- kpi_cards.add_trace(go.Indicator(
59
- mode="number",
60
- value=round(kpi_vol, 2),
61
- title={"text": "Volatility %"},
62
- domain={'row': 0, 'column': 1}
63
- ))
64
- kpi_cards.add_trace(go.Indicator(
65
- mode="number",
66
- value=round(sharpe, 2),
67
- title={"text": "Sharpe Ratio"},
68
- domain={'row': 0, 'column': 2}
69
- ))
70
- kpi_cards.update_layout(grid={'rows': 1, 'columns': 3}, template="plotly_dark")
71
-
72
- # === PnL Timeline Chart ===
73
- pnl_fig = px.line(df, y="value", title="Absolute PnL Over Time")
74
- pnl_fig.update_layout(template="plotly_dark", height=400)
75
-
76
- # === Histogram Volatility ===
77
- vol_fig = px.histogram(df["value"].pct_change().dropna(),
78
- nbins=30, title="Daily Returns Distribution")
79
- vol_fig.update_layout(template="plotly_dark", height=300)
80
-
81
- return kpi_cards, pnl_fig, vol_fig