globalsy_mvp/api.py
belikovme 391757d581 Рефакторинг конфигурации и структуры проекта
- Обновлен config.py с оптимизированными словарями секторов и индексов
- Удалены устаревшие классы exchange.py и moex_class.py
- Модернизирован moex_history.py с улучшенной логикой получения данных
- Обновлен requirements.txt с современными зависимостями для финансовой платформы
- Упрощен open_router.ipynb с фокусом на экономических темах
2025-03-12 17:01:25 +07:00

236 lines
9.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import logging
from models import FinancialDataManager, NewsManager, AnalyticsManager
from config import API_CONFIG
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("api.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger("api")
# Создаем экземпляр FastAPI
app = FastAPI(
title="Финансово-аналитическая платформа API",
description="API для доступа к финансовым данным, новостям и аналитике",
version="1.0.0"
)
# Настройка CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # В продакшене следует указать конкретные домены
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Инициализация менеджеров данных
financial_manager = FinancialDataManager()
news_manager = NewsManager()
analytics_manager = AnalyticsManager()
# Модели данных для API
class SectorData(BaseModel):
sector: str
date: str
open: float
high: float
low: float
close: float
volume: float
class TickerData(BaseModel):
ticker: str
sector: str
date: str
open: float
high: float
low: float
close: float
volume: float
class NewsItem(BaseModel):
id: int
date: str
topic: Optional[str] = None
title: str
content: str
url: Optional[str] = None
class AnalyticsItem(BaseModel):
id: int
date: str
summary: str
business_problems: Optional[str] = None
solutions: Optional[str] = None
sentiment: Optional[float] = None
# Маршруты API
@app.get("/")
async def root():
"""Корневой маршрут API"""
return {
"message": "Добро пожаловать в API финансово-аналитической платформы",
"version": "1.0.0",
"endpoints": [
"/sectors",
"/sectors/{sector}",
"/tickers",
"/tickers/{ticker}",
"/news",
"/news/topics",
"/analytics"
]
}
# Маршруты для финансовых данных
@app.get("/sectors", response_model=List[str])
async def get_sectors():
"""Получить список всех доступных секторов"""
try:
sectors = financial_manager.get_all_sectors()
return sectors
except Exception as e:
logger.error(f"Ошибка при получении списка секторов: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
@app.get("/sectors/{sector}", response_model=List[Dict[str, Any]])
async def get_sector_data(
sector: str,
start_date: Optional[str] = Query(None, description="Начальная дата в формате YYYY-MM-DD"),
end_date: Optional[str] = Query(None, description="Конечная дата в формате YYYY-MM-DD")
):
"""Получить данные по указанному сектору за период"""
try:
# Если даты не указаны, используем последние 30 дней
if not end_date:
end_date = datetime.now().strftime("%Y-%m-%d")
if not start_date:
start_date = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
data = financial_manager.get_sector_data(sector, start_date, end_date)
if not data:
raise HTTPException(status_code=404, detail=f"Данные для сектора {sector} не найдены")
return data
except HTTPException:
raise
except Exception as e:
logger.error(f"Ошибка при получении данных сектора {sector}: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
@app.get("/tickers", response_model=Dict[str, List[str]])
async def get_tickers():
"""Получить список всех доступных тикеров, сгруппированных по секторам"""
try:
tickers = financial_manager.get_all_tickers()
return tickers
except Exception as e:
logger.error(f"Ошибка при получении списка тикеров: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
@app.get("/tickers/{ticker}", response_model=List[Dict[str, Any]])
async def get_ticker_data(
ticker: str,
start_date: Optional[str] = Query(None, description="Начальная дата в формате YYYY-MM-DD"),
end_date: Optional[str] = Query(None, description="Конечная дата в формате YYYY-MM-DD")
):
"""Получить данные по указанному тикеру за период"""
try:
# Если даты не указаны, используем последние 30 дней
if not end_date:
end_date = datetime.now().strftime("%Y-%m-%d")
if not start_date:
start_date = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%d")
data = financial_manager.get_ticker_data(ticker, start_date, end_date)
if not data:
raise HTTPException(status_code=404, detail=f"Данные для тикера {ticker} не найдены")
return data
except HTTPException:
raise
except Exception as e:
logger.error(f"Ошибка при получении данных тикера {ticker}: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
# Маршруты для новостей
@app.get("/news", response_model=List[Dict[str, Any]])
async def get_news(
start_date: Optional[str] = Query(None, description="Начальная дата в формате YYYY-MM-DD"),
end_date: Optional[str] = Query(None, description="Конечная дата в формате YYYY-MM-DD"),
topic: Optional[str] = Query(None, description="Фильтр по теме новости")
):
"""Получить новости за указанный период с возможностью фильтрации по теме"""
try:
# Если даты не указаны, используем последние 7 дней
if not end_date:
end_date = datetime.now().strftime("%Y-%m-%d")
if not start_date:
start_date = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
if topic:
news = news_manager.get_news_by_topic(topic, start_date, end_date)
else:
news = news_manager.get_news_by_date(start_date, end_date)
return news
except Exception as e:
logger.error(f"Ошибка при получении новостей: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
@app.get("/news/topics", response_model=List[str])
async def get_news_topics():
"""Получить список всех доступных тем новостей"""
try:
topics = news_manager.get_topics()
return topics
except Exception as e:
logger.error(f"Ошибка при получении списка тем новостей: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
# Маршруты для аналитики
@app.get("/analytics", response_model=List[Dict[str, Any]])
async def get_analytics(
start_date: Optional[str] = Query(None, description="Начальная дата в формате YYYY-MM-DD"),
end_date: Optional[str] = Query(None, description="Конечная дата в формате YYYY-MM-DD")
):
"""Получить аналитику за указанный период"""
try:
# Если даты не указаны, используем последние 7 дней
if not end_date:
end_date = datetime.now().strftime("%Y-%m-%d")
if not start_date:
start_date = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
analytics = analytics_manager.get_analytics_by_date(start_date, end_date)
return analytics
except Exception as e:
logger.error(f"Ошибка при получении аналитики: {str(e)}")
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
# Запуск сервера
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"api:app",
host=API_CONFIG['host'],
port=API_CONFIG['port'],
reload=True
)