Spaces:
Sleeping
Sleeping
QAway-to
commited on
Commit
·
e63adc7
1
Parent(s):
d5241ad
without k_metrics
Browse files- SYSTEM_PROMPT.py +20 -0
- analyzer.py +40 -24
- app.py +49 -49
- fetch.py +33 -0
SYSTEM_PROMPT.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
SYSTEM_PROMPT = (
|
| 3 |
+
"Ты — опытный финансовый аналитик и инвестиционный советник. "
|
| 4 |
+
"Твоя задача — анализировать ключевые метрики инвестиционных портфелей и давать содержательные, прямолинейные и живые комментарии, которые легко воспринимаются даже непрофессионалами. "
|
| 5 |
+
"Твоя сила — в способности объяснять сложное просто, без лишней терминологии и формальной канцелярщины.\n\n"
|
| 6 |
+
|
| 7 |
+
"Формируй ответы уверенно, избегай сухих, повторяющихся формулировок. Не начинай каждый абзац с 'Это означает...', 'Это говорит о том, что...', 'Это хороший показатель...' — "
|
| 8 |
+
"такие обороты утомляют и делают речь однообразной. Вариативность важна. Используй живой язык, строй выводы, делай переходы между метриками логично и естественно.\n\n"
|
| 9 |
+
|
| 10 |
+
"Если метрика высокая — обозначь, чем это выгодно для инвестора. Если слабая — не скрывай риски и ограничения. Не бойся критиковать стратегию. "
|
| 11 |
+
"Будь честным, как на встрече с клиентом: без лишнего позитивного окраса, только по делу. Допускается лёгкая ирония, если она помогает донести суть.\n\n"
|
| 12 |
+
|
| 13 |
+
"Ты можешь делать предположения и гипотезы: например, как стратегия поведёт себя в растущем рынке, при кризисе, при высокой инфляции или волатильности. "
|
| 14 |
+
"Портфель может быть описан как 'агрессивный', 'консервативный', 'высокочастотный', 'рискованный', 'подходит для пассивного дохода' и т.д. — не бойся давать такие оценки.\n\n"
|
| 15 |
+
|
| 16 |
+
"Пиши как будто общаешься напрямую с человеком, который хочет понять: стоит ли ему доверять деньги этой стратегии. В конце — можешь кратко подытожить общую картину и настроение по портфелю.\n\n"
|
| 17 |
+
|
| 18 |
+
"Всё это должно выглядеть как профессиональный, но понятный разговор — не как слайд с конференции. В очередном описании ключевой метрики, старайся применять иные формулировки, нежели те, которые применял для описания предыдущей."
|
| 19 |
+
"При сравнении двух портфелей, СТРОГО используй 'портфель А' и портфель B', выделяй сильные и слабые стороны каждого, делай акцент на различиях и потенциальных рисках."
|
| 20 |
+
)
|
analyzer.py
CHANGED
|
@@ -1,32 +1,48 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
|
| 9 |
-
def fetch_metrics(portfolio_id: str) -> dict | None:
|
| 10 |
try:
|
|
|
|
| 11 |
url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
|
| 12 |
response = requests.get(url)
|
| 13 |
-
response.
|
| 14 |
-
extended =
|
| 15 |
-
keys = [
|
| 16 |
-
# 🔹 Доходность и риск
|
| 17 |
-
"alphaRatio", "betaRatio", "cagr", "sharpe", "sortino", "volatility",
|
| 18 |
-
"kSortino", "kCalmar", "kSharpe",
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
|
|
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import SYSTEM_PROMPT
|
| 2 |
+
# Анализ одного портфеля
|
| 3 |
+
def analyze_portfolio_streaming(text: str):
|
| 4 |
+
portfolio_id = extract_portfolio_id(text)
|
| 5 |
+
if not portfolio_id:
|
| 6 |
+
yield "❗ Укажите корректный portfolioId или ссылку."
|
| 7 |
+
return
|
| 8 |
|
|
|
|
| 9 |
try:
|
| 10 |
+
# Получение JSON по API
|
| 11 |
url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
|
| 12 |
response = requests.get(url)
|
| 13 |
+
json_data = response.json()
|
| 14 |
+
extended = json_data.get("data", {}).get("extended", {})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
if not extended:
|
| 17 |
+
yield "❗ Метрики не найдены в ответе API."
|
| 18 |
+
return
|
| 19 |
|
| 20 |
+
# Передаём все числовые метрики, без фильтрации
|
| 21 |
+
metrics = {k: v for k, v in extended.items() if isinstance(v, (int, float))}
|
| 22 |
+
if not metrics:
|
| 23 |
+
yield "❗ Нет числовых метрик для анализа."
|
| 24 |
+
return
|
| 25 |
|
| 26 |
+
metrics_text = ", ".join([f"{k}: {v}" for k, v in metrics.items()])
|
| 27 |
+
prompt = f"""Вот метрики портфеля: {metrics_text}.
|
| 28 |
+
Проанализируй их и объясни сильные и слабые стороны на русском языке, как финансовый аналитик."""
|
| 29 |
|
| 30 |
+
# Запрос к модели
|
| 31 |
+
response_llm = client.chat.completions.create(
|
| 32 |
+
model="nasiruddin15/Mistral-dolphin-2.8-grok-instract-2-7B-slerp",
|
| 33 |
+
messages=[
|
| 34 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 35 |
+
{"role": "user", "content": prompt}
|
| 36 |
+
],
|
| 37 |
+
stream=True
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
partial = ""
|
| 41 |
+
for chunk in response_llm:
|
| 42 |
+
delta = chunk.choices[0].delta.content
|
| 43 |
+
if delta:
|
| 44 |
+
partial += delta
|
| 45 |
+
yield partial
|
| 46 |
+
|
| 47 |
+
except Exception as e:
|
| 48 |
+
yield f"❌ Ошибка при обработке: {e}"
|
app.py
CHANGED
|
@@ -2,8 +2,8 @@ import gradio as gr
|
|
| 2 |
import requests
|
| 3 |
import re
|
| 4 |
import os
|
| 5 |
-
from analyzer import
|
| 6 |
-
|
| 7 |
from openai import OpenAI
|
| 8 |
|
| 9 |
api_key = os.getenv("featherless") #
|
|
@@ -105,7 +105,7 @@ def extract_portfolio_id(text: str) -> str | None:
|
|
| 105 |
# response_llm = client.chat.completions.create(
|
| 106 |
# model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 107 |
# messages=[
|
| 108 |
-
# {"role": "system", "content": SYSTEM_PROMPT},
|
| 109 |
# {"role": "user", "content": prompt}
|
| 110 |
# ],
|
| 111 |
# stream=True
|
|
@@ -120,52 +120,52 @@ def extract_portfolio_id(text: str) -> str | None:
|
|
| 120 |
# yield partial
|
| 121 |
|
| 122 |
# Анализ одного портфеля
|
| 123 |
-
def analyze_portfolio_streaming(text: str):
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
Проанализируй их и объясни сильные и слабые стороны на русском языке, как финансовый аналитик."""
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
|
| 170 |
|
| 171 |
# Сравнение двух портфелей
|
|
|
|
| 2 |
import requests
|
| 3 |
import re
|
| 4 |
import os
|
| 5 |
+
from analyzer import analyze_portfolio_streaming
|
| 6 |
+
from fetch import fetch_metrics
|
| 7 |
from openai import OpenAI
|
| 8 |
|
| 9 |
api_key = os.getenv("featherless") #
|
|
|
|
| 105 |
# response_llm = client.chat.completions.create(
|
| 106 |
# model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 107 |
# messages=[
|
| 108 |
+
# {"role": "system", "content": SYSTEM_PROMPT.py},
|
| 109 |
# {"role": "user", "content": prompt}
|
| 110 |
# ],
|
| 111 |
# stream=True
|
|
|
|
| 120 |
# yield partial
|
| 121 |
|
| 122 |
# Анализ одного портфеля
|
| 123 |
+
# def analyze_portfolio_streaming(text: str):
|
| 124 |
+
# portfolio_id = extract_portfolio_id(text)
|
| 125 |
+
# if not portfolio_id:
|
| 126 |
+
# yield "❗ Укажите корректный portfolioId или ссылку."
|
| 127 |
+
# return
|
| 128 |
+
#
|
| 129 |
+
# try:
|
| 130 |
+
# # Получение JSON по API
|
| 131 |
+
# url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
|
| 132 |
+
# response = requests.get(url)
|
| 133 |
+
# json_data = response.json()
|
| 134 |
+
# extended = json_data.get("data", {}).get("extended", {})
|
| 135 |
+
#
|
| 136 |
+
# if not extended:
|
| 137 |
+
# yield "❗ Метрики не найдены в ответе API."
|
| 138 |
+
# return
|
| 139 |
+
#
|
| 140 |
+
# # Передаём все числовые метрики, без фильтрации
|
| 141 |
+
# metrics = {k: v for k, v in extended.items() if isinstance(v, (int, float))}
|
| 142 |
+
# if not metrics:
|
| 143 |
+
# yield "❗ Нет числовых метрик для анализа."
|
| 144 |
+
# return
|
| 145 |
+
#
|
| 146 |
+
# metrics_text = ", ".join([f"{k}: {v}" for k, v in metrics.items()])
|
| 147 |
+
# prompt = f"""Вот метрики портфеля: {metrics_text}.
|
| 148 |
+
# Проанализируй их и объясни сильные и слабые стороны на русском языке, как финансовый аналитик."""
|
| 149 |
+
#
|
| 150 |
+
# # Запрос к модели
|
| 151 |
+
# response_llm = client.chat.completions.create(
|
| 152 |
+
# model="nasiruddin15/Mistral-dolphin-2.8-grok-instract-2-7B-slerp",
|
| 153 |
+
# messages=[
|
| 154 |
+
# {"role": "system", "content": SYSTEM_PROMPT},
|
| 155 |
+
# {"role": "user", "content": prompt}
|
| 156 |
+
# ],
|
| 157 |
+
# stream=True
|
| 158 |
+
# )
|
| 159 |
+
#
|
| 160 |
+
# partial = ""
|
| 161 |
+
# for chunk in response_llm:
|
| 162 |
+
# delta = chunk.choices[0].delta.content
|
| 163 |
+
# if delta:
|
| 164 |
+
# partial += delta
|
| 165 |
+
# yield partial
|
| 166 |
+
#
|
| 167 |
+
# except Exception as e:
|
| 168 |
+
# yield f"❌ Ошибка при обработке: {e}"
|
| 169 |
|
| 170 |
|
| 171 |
# Сравнение двух портфелей
|
fetch.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Извлечение UUID из строки
|
| 2 |
+
def extract_portfolio_id(text: str) -> str | None:
|
| 3 |
+
match = re.search(
|
| 4 |
+
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}",
|
| 5 |
+
text
|
| 6 |
+
)
|
| 7 |
+
return match.group(0) if match else None
|
| 8 |
+
|
| 9 |
+
def fetch_metrics(portfolio_id: str) -> dict | None:
|
| 10 |
+
try:
|
| 11 |
+
url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
|
| 12 |
+
response = requests.get(url)
|
| 13 |
+
response.raise_for_status()
|
| 14 |
+
extended = response.json().get("data", {}).get("extended", {})
|
| 15 |
+
keys = [
|
| 16 |
+
# 🔹 Доходность и риск
|
| 17 |
+
"alphaRatio", "betaRatio", "cagr", "sharpe", "sortino", "volatility",
|
| 18 |
+
"kSortino", "kCalmar", "kSharpe",
|
| 19 |
+
|
| 20 |
+
# 🔹 Просадки
|
| 21 |
+
"maxDD", "mddDuration", "maxBalance",
|
| 22 |
+
|
| 23 |
+
# 🔹 Поведение стратегии
|
| 24 |
+
"losingDays", "winningDays", "selfProfitRate",
|
| 25 |
+
|
| 26 |
+
# 🔹 Актуальные показатели доходности
|
| 27 |
+
"lastWeekNetProfit", "lastMonthNetProfit", "lastQuarterGrowth", "lastYearNetGrowth"
|
| 28 |
+
]
|
| 29 |
+
|
| 30 |
+
return {k: extended[k] for k in keys if isinstance(extended.get(k), (int, float))}
|
| 31 |
+
except:
|
| 32 |
+
return None
|
| 33 |
+
|