feat: Add core trading modules for risk management, backtesting, and execution algorithms, alongside a new ML transparency widget and related frontend dependencies.
Some checks are pending
Documentation / build-docs (push) Waiting to run
Tests / test (macos-latest, 3.11) (push) Waiting to run
Tests / test (macos-latest, 3.12) (push) Waiting to run
Tests / test (macos-latest, 3.13) (push) Waiting to run
Tests / test (macos-latest, 3.14) (push) Waiting to run
Tests / test (ubuntu-latest, 3.11) (push) Waiting to run
Tests / test (ubuntu-latest, 3.12) (push) Waiting to run
Tests / test (ubuntu-latest, 3.13) (push) Waiting to run
Tests / test (ubuntu-latest, 3.14) (push) Waiting to run
Some checks are pending
Documentation / build-docs (push) Waiting to run
Tests / test (macos-latest, 3.11) (push) Waiting to run
Tests / test (macos-latest, 3.12) (push) Waiting to run
Tests / test (macos-latest, 3.13) (push) Waiting to run
Tests / test (macos-latest, 3.14) (push) Waiting to run
Tests / test (ubuntu-latest, 3.11) (push) Waiting to run
Tests / test (ubuntu-latest, 3.12) (push) Waiting to run
Tests / test (ubuntu-latest, 3.13) (push) Waiting to run
Tests / test (ubuntu-latest, 3.14) (push) Waiting to run
This commit is contained in:
@@ -8,6 +8,7 @@ import pandas as pd
|
||||
|
||||
from src.core.database import MarketData, get_database
|
||||
from src.data.pricing_service import get_pricing_service
|
||||
from src.data.indicators import get_indicators
|
||||
from src.core.config import get_config
|
||||
|
||||
router = APIRouter()
|
||||
@@ -278,3 +279,130 @@ async def get_spread_data(
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/indicators/{symbol:path}")
|
||||
async def get_indicators_data(
|
||||
symbol: str,
|
||||
timeframe: str = "1h",
|
||||
limit: int = 100,
|
||||
exchange: str = "coinbase",
|
||||
indicators: str = Query("", description="Comma-separated list of indicators (e.g., 'sma_20,ema_20,rsi,macd,bollinger_bands')")
|
||||
):
|
||||
"""Get OHLCV data with technical indicators for a symbol.
|
||||
|
||||
Supported indicators:
|
||||
- sma_<period>: Simple Moving Average (e.g., sma_20, sma_50)
|
||||
- ema_<period>: Exponential Moving Average (e.g., ema_20, ema_50)
|
||||
- rsi: Relative Strength Index
|
||||
- macd: MACD (returns macd, signal, histogram)
|
||||
- bollinger_bands: Bollinger Bands (returns upper, middle, lower)
|
||||
- atr: Average True Range
|
||||
- obv: On Balance Volume
|
||||
- adx: Average Directional Index
|
||||
"""
|
||||
from sqlalchemy import select
|
||||
try:
|
||||
# Fetch OHLCV data first (reuse existing logic)
|
||||
ohlcv_data = []
|
||||
try:
|
||||
db = get_database()
|
||||
async with db.get_session() as session:
|
||||
stmt = select(MarketData).filter_by(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
exchange=exchange
|
||||
).order_by(MarketData.timestamp.desc()).limit(limit)
|
||||
|
||||
result = await session.execute(stmt)
|
||||
data = result.scalars().all()
|
||||
|
||||
if data:
|
||||
ohlcv_data = [
|
||||
{
|
||||
"time": int(d.timestamp.timestamp()),
|
||||
"open": float(d.open),
|
||||
"high": float(d.high),
|
||||
"low": float(d.low),
|
||||
"close": float(d.close),
|
||||
"volume": float(d.volume)
|
||||
}
|
||||
for d in reversed(data)
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# If no DB data, fetch from pricing service
|
||||
if not ohlcv_data:
|
||||
try:
|
||||
pricing_service = get_pricing_service()
|
||||
ohlcv_raw = pricing_service.get_ohlcv(
|
||||
symbol=symbol,
|
||||
timeframe=timeframe,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
if ohlcv_raw:
|
||||
ohlcv_data = [
|
||||
{
|
||||
"time": int(candle[0] / 1000),
|
||||
"open": float(candle[1]),
|
||||
"high": float(candle[2]),
|
||||
"low": float(candle[3]),
|
||||
"close": float(candle[4]),
|
||||
"volume": float(candle[5])
|
||||
}
|
||||
for candle in ohlcv_raw
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not ohlcv_data:
|
||||
return {"ohlcv": [], "indicators": {}}
|
||||
|
||||
# Convert to DataFrame for indicator calculation
|
||||
df = pd.DataFrame(ohlcv_data)
|
||||
df.set_index('time', inplace=True)
|
||||
|
||||
# Prepare DataFrame for indicators (needs columns: open, high, low, close, volume)
|
||||
df_ind = pd.DataFrame({
|
||||
'open': df['open'],
|
||||
'high': df['high'],
|
||||
'low': df['low'],
|
||||
'close': df['close'],
|
||||
'volume': df['volume']
|
||||
})
|
||||
|
||||
# Parse indicator list
|
||||
indicator_list = [ind.strip() for ind in indicators.split(',') if ind.strip()] if indicators else []
|
||||
|
||||
# Calculate indicators
|
||||
indicators_calc = get_indicators()
|
||||
if indicator_list:
|
||||
df_with_indicators = indicators_calc.calculate_all(df_ind, indicators=indicator_list)
|
||||
else:
|
||||
# Default indicators if none specified
|
||||
df_with_indicators = indicators_calc.calculate_all(df_ind)
|
||||
|
||||
# Build response
|
||||
result_ohlcv = df.reset_index().to_dict('records')
|
||||
|
||||
# Extract indicator data
|
||||
indicator_data = {}
|
||||
for col in df_with_indicators.columns:
|
||||
if col not in ['open', 'high', 'low', 'close', 'volume']:
|
||||
# Convert NaN to None for JSON serialization
|
||||
values = df_with_indicators[col].replace({pd.NA: None, pd.NaT: None}).tolist()
|
||||
indicator_data[col] = [
|
||||
float(v) if v is not None and not pd.isna(v) else None
|
||||
for v in values
|
||||
]
|
||||
|
||||
return {
|
||||
"ohlcv": result_ohlcv,
|
||||
"indicators": indicator_data,
|
||||
"times": [int(t) for t in df.index.tolist()]
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user