import logging from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.api.dependencies import get_db_session from app.models.entities import KlineModel, TradeModel logger = logging.getLogger(__name__) kline_router = APIRouter(prefix="/v1/charts") @kline_router.get("/klines") async def get_market_klines( market_address: str = Query(..., description="预测市场合约地址"), outcome_index: int = Query(0, description="结果选项序号 (0-based)"), timeframe: str = Query("5s", description="时间粒度: 1s, 5s, 1m, 1h"), limit: int = Query(1000, le=5000, description="返回的最大 K 线根数"), db: AsyncSession = Depends(get_db_session), ): """ 【TradingView 标准 OHLCV K 线历史接口】 返回由 Indexer 从链上真实成交事件确定性聚合的 K 线。 无成交历史时返回空数组(绝不生成假数据)。 """ m_addr = market_address.lower() # 当前仅支持 5 秒基准聚合(Indexer 按 5s 窗口写入),其他粒度在后续版本扩展 rows = ( await db.execute( select(KlineModel) .where( KlineModel.market_address == m_addr, KlineModel.outcome_index == outcome_index, ) .order_by(KlineModel.bar_time.asc()) .limit(limit) ) ).scalars().all() bars = [ { "time": row.bar_time, "open": float(row.open), "high": float(row.high), "low": float(row.low), "close": float(row.close), "volume": float(row.volume), } for row in rows ] return { "market": m_addr, "outcome_index": outcome_index, "timeframe": timeframe, "has_trades": len(bars) > 0, "bars": bars, } @kline_router.get("/trades") async def get_market_trades( market_address: str = Query(..., description="预测市场合约地址"), outcome_index: int = Query(0, description="结果选项序号 (0-based)"), limit: int = Query(100, le=500), db: AsyncSession = Depends(get_db_session), ): """真实链上逐笔成交明细(由 Indexer 写入)""" m_addr = market_address.lower() rows = ( await db.execute( select(TradeModel) .where( TradeModel.market_address == m_addr, TradeModel.outcome_index == outcome_index, ) .order_by(TradeModel.ts.desc()) .limit(limit) ) ).scalars().all() trades = [ { "tx_hash": row.tx_hash, "type": row.trade_type, "price": float(row.price), "volume": float(row.volume), "block_number": row.block_number, "ts": row.ts, } for row in rows ] return {"market": m_addr, "outcome_index": outcome_index, "trades": trades}