QAway-to commited on
Commit
d97fa05
·
1 Parent(s): 9e2ccc7
Files changed (1) hide show
  1. app.py +103 -54
app.py CHANGED
@@ -9,7 +9,7 @@ client = OpenAI(
9
  api_key="rc_2b641fdc25954733a5ef7ccd10ce6a8e1db603e386a0fca262626daf21562b4b"
10
  )
11
 
12
- # Функция извлечения UUID
13
  def extract_portfolio_id(text: str) -> str | None:
14
  match = re.search(
15
  r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
@@ -17,69 +17,118 @@ def extract_portfolio_id(text: str) -> str | None:
17
  )
18
  return match.group(0) if match else None
19
 
20
- # Генератор — стриминговый вывод анализа
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def analyze_portfolio_streaming(text: str):
22
  portfolio_id = extract_portfolio_id(text)
23
  if not portfolio_id:
24
- yield "❗ Укажите корректный portfolioId или ссылку на портфель."
25
  return
26
 
27
- url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
 
 
 
28
 
29
- try:
30
- response = requests.get(url)
31
- response.raise_for_status()
32
- json_data = response.json()
33
- extended = json_data.get("data", {}).get("extended", {})
34
-
35
- metrics_keys = ["alphaRatio", "betaRatio", "cagr", "sharpe", "sortino", "volatility"]
36
- filtered = {k: v for k, v in extended.items() if k in metrics_keys and isinstance(v, (int, float))}
37
-
38
- if not filtered:
39
- yield "❗ Ключевые метрики отсутствуют в ответе API."
40
- return
41
-
42
- metrics_text = ", ".join([f"{k}: {v}" for k, v in filtered.items()])
43
- prompt = f"""Вот метрики портфеля: {metrics_text}.
44
- Проанализируй их и объясни сильные и слабые стороны на русском языке, как финансовый аналитик."""
45
-
46
- response_llm = client.chat.completions.create(
47
- model="meta-llama/Meta-Llama-3.1-8B-Instruct",
48
- messages=[
49
- {"role": "system", "content": "Ты — финансовый аналитик."},
50
- {"role": "user", "content": prompt}
51
- ],
52
- stream=True
53
- )
54
-
55
- partial = ""
56
- for chunk in response_llm:
57
- delta = chunk.choices[0].delta.content
58
- if delta:
59
- partial += delta
60
- yield partial
61
-
62
- except Exception as e:
63
- yield f"❌ Ошибка: {e}"
64
-
65
- # Интерфейс Gradio (Blocks)
66
- with gr.Blocks() as demo:
67
- gr.Markdown("## 🧠 Анализ инвестиционного портфеля Tradelink")
68
 
69
- with gr.Row():
70
- portfolio_input = gr.Textbox(
71
- label="Введите ссылку или portfolioId",
72
- placeholder="ea856c1b-65ee-4177-b8d1-e5ab0d610a91"
73
- )
74
- analyze_button = gr.Button("🔍 Проанализировать")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
- output_box = gr.Textbox(label="📈 Результат анализа", lines=15)
 
77
 
78
- analyze_button.click(
79
- fn=analyze_portfolio_streaming,
80
- inputs=portfolio_input,
81
- outputs=output_box
 
 
 
 
 
82
  )
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  if __name__ == "__main__":
85
  demo.launch()
 
9
  api_key="rc_2b641fdc25954733a5ef7ccd10ce6a8e1db603e386a0fca262626daf21562b4b"
10
  )
11
 
12
+ # Извлечение UUID из строки
13
  def extract_portfolio_id(text: str) -> str | None:
14
  match = re.search(
15
  r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
 
17
  )
18
  return match.group(0) if match else None
19
 
20
+ # Получение метрик
21
+ def fetch_metrics(portfolio_id: str) -> dict | None:
22
+ try:
23
+ url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
24
+ response = requests.get(url)
25
+ response.raise_for_status()
26
+ extended = response.json().get("data", {}).get("extended", {})
27
+ keys = [
28
+ # 🔹 Доходность и риск
29
+ "alphaRatio", "betaRatio", "cagr", "sharpe", "sortino", "volatility",
30
+ "kSortino", "kCalmar", "kSharpe",
31
+
32
+ # 🔹 Просадки
33
+ "maxDD", "mddDuration", "maxBalance",
34
+
35
+ # 🔹 Поведение стратегии
36
+ "losingDays", "winningDays", "selfProfitRate",
37
+
38
+ # 🔹 Актуальные показатели доходности
39
+ "lastWeekNetProfit", "lastMonthNetProfit", "lastQuarterGrowth", "lastYearNetGrowth"
40
+ ]
41
+
42
+ return {k: extended[k] for k in keys if isinstance(extended.get(k), (int, float))}
43
+ except:
44
+ return None
45
+
46
+ # Анализ одного портфеля
47
  def analyze_portfolio_streaming(text: str):
48
  portfolio_id = extract_portfolio_id(text)
49
  if not portfolio_id:
50
+ yield "❗ Укажите корректный portfolioId или ссылку."
51
  return
52
 
53
+ metrics = fetch_metrics(portfolio_id)
54
+ if not metrics:
55
+ yield "❗ Не удалось получить метрики портфеля."
56
+ return
57
 
58
+ metrics_text = ", ".join([f"{k}: {v}" for k, v in metrics.items()])
59
+ prompt = f"Вот метрики портфеля: {metrics_text}. Проанализируй их и объясни сильные и слабые стороны на русском языке, как финансовый аналитик."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
+ response_llm = client.chat.completions.create(
62
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct",
63
+ messages=[
64
+ {"role": "system", "content": "Ты — финансовый аналитик."},
65
+ {"role": "user", "content": prompt}
66
+ ],
67
+ stream=True
68
+ )
69
+
70
+ partial = ""
71
+ for chunk in response_llm:
72
+ delta = chunk.choices[0].delta.content
73
+ if delta:
74
+ partial += delta
75
+ yield partial
76
+
77
+ # Сравнение двух портфелей
78
+ def compare_portfolios_streaming(text1: str, text2: str):
79
+ id1 = extract_portfolio_id(text1)
80
+ id2 = extract_portfolio_id(text2)
81
+ if not id1 or not id2:
82
+ yield "❗ Один или оба portfolioId некорректны."
83
+ return
84
+
85
+ m1 = fetch_metrics(id1)
86
+ m2 = fetch_metrics(id2)
87
+ if not m1 or not m2:
88
+ yield "❗ Не удалось получить метрики одного из портфелей."
89
+ return
90
+
91
+ m1_text = ", ".join([f"{k}: {v}" for k, v in m1.items()])
92
+ m2_text = ", ".join([f"{k}: {v}" for k, v in m2.items()])
93
+ prompt = f"""Сравни два инвестиционных портфеля:
94
 
95
+ Портфель 1: {m1_text}
96
+ Портфель 2: {m2_text}
97
 
98
+ Сделай сравнительный анализ на русском языке как финансовый аналитик. Укажи, какой портфель сильнее, в чём риски, где преимущества."""
99
+
100
+ response = client.chat.completions.create(
101
+ model="meta-llama/Meta-Llama-3.1-8B-Instruct",
102
+ messages=[
103
+ {"role": "system", "content": "Ты — финансовый аналитик."},
104
+ {"role": "user", "content": prompt}
105
+ ],
106
+ stream=True
107
  )
108
 
109
+ partial = ""
110
+ for chunk in response:
111
+ delta = chunk.choices[0].delta.content
112
+ if delta:
113
+ partial += delta
114
+ yield partial
115
+
116
+ # Gradio интерфейс
117
+ with gr.Blocks() as demo:
118
+ gr.Markdown("## 🧠 Анализ и сравнение инвестиционных портфелей Tradelink")
119
+
120
+ with gr.Tab("📊 Анализ"):
121
+ portfolio_input = gr.Textbox(label="Введите ссылку или portfolioId", placeholder="ea856c1b-...")
122
+ analyze_button = gr.Button("🔍 Проанализировать")
123
+ output_box = gr.Textbox(label="📈 Результат анализа", lines=15)
124
+ analyze_button.click(fn=analyze_portfolio_streaming, inputs=portfolio_input, outputs=output_box)
125
+
126
+ with gr.Tab("⚖️ Сравнение"):
127
+ compare_input_1 = gr.Textbox(label="Портфель 1", placeholder="ea856c1b-...")
128
+ compare_input_2 = gr.Textbox(label="Портфель 2", placeholder="d52f55cc-...")
129
+ compare_button = gr.Button("📊 Сравнить")
130
+ compare_output = gr.Textbox(label="📉 Результат сравнения", lines=20)
131
+ compare_button.click(fn=compare_portfolios_streaming, inputs=[compare_input_1, compare_input_2], outputs=compare_output)
132
+
133
  if __name__ == "__main__":
134
  demo.launch()