Automated Action 3a29a346a9 Initial implementation of cryptocurrency data API service using CoinCap API
* Created FastAPI application structure
* Added database models for assets, exchanges, markets, and rates
* Integrated with CoinCap API
* Implemented REST API endpoints
* Setup SQLite persistence with Alembic migrations
* Added comprehensive documentation

Generated with BackendIM... (backend.im)
2025-05-14 12:18:27 +00:00

117 lines
3.9 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query, Path, status
from sqlalchemy.orm import Session
from typing import Optional, List
import time
from app.services.coincap_api import coincap_api
from app.core.database import get_db
from app.api.schemas.asset import (
Asset, AssetCreate,
AssetResponse, AssetsResponse,
AssetHistoryResponse
)
router = APIRouter()
@router.get("", response_model=AssetsResponse)
async def get_assets(
search: Optional[str] = None,
ids: Optional[str] = None,
limit: Optional[int] = Query(100, le=2000),
offset: Optional[int] = Query(0, ge=0),
db: Session = Depends(get_db)
):
"""
Get a list of assets with optional filters
"""
try:
response = await coincap_api.get_assets(search, ids, limit, offset)
return response
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to fetch assets: {str(e)}"
)
@router.get("/{asset_id}", response_model=AssetResponse)
async def get_asset(
asset_id: str = Path(..., description="The asset ID (slug) to retrieve"),
db: Session = Depends(get_db)
):
"""
Get details for a specific asset by ID
"""
try:
response = await coincap_api.get_asset(asset_id)
return response
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to fetch asset {asset_id}: {str(e)}"
)
@router.get("/{asset_id}/markets", response_model=dict)
async def get_asset_markets(
asset_id: str = Path(..., description="The asset ID (slug) to retrieve markets for"),
limit: Optional[int] = Query(100, le=2000),
offset: Optional[int] = Query(0, ge=0),
db: Session = Depends(get_db)
):
"""
Get markets for a specific asset
"""
try:
response = await coincap_api.get_asset_markets(asset_id, limit, offset)
return response
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to fetch markets for asset {asset_id}: {str(e)}"
)
@router.get("/{asset_id}/history", response_model=AssetHistoryResponse)
async def get_asset_history(
asset_id: str = Path(..., description="The asset ID (slug) to retrieve history for"),
interval: str = Query(..., description="Interval choices: m1, m5, m15, m30, h1, h2, h6, h12, d1"),
start: Optional[int] = Query(None, description="UNIX time in milliseconds"),
end: Optional[int] = Query(None, description="UNIX time in milliseconds"),
db: Session = Depends(get_db)
):
"""
Get historical data for a specific asset
"""
# Validate interval choices
valid_intervals = ["m1", "m5", "m15", "m30", "h1", "h2", "h6", "h12", "d1"]
if interval not in valid_intervals:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid interval. Choose from: {', '.join(valid_intervals)}"
)
# Both start and end must be provided if one is provided
if (start is None and end is not None) or (start is not None and end is None):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Both 'start' and 'end' parameters must be provided together"
)
try:
response = await coincap_api.get_asset_history(asset_id, interval, start, end)
return response
except HTTPException as e:
raise e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to fetch history for asset {asset_id}: {str(e)}"
)