2025-06-07 12:44:14 +00:00

148 lines
5.2 KiB
Python

from datetime import datetime
from typing import Any, Optional
from fastapi import APIRouter, Depends, Query, HTTPException, status, Response
from sqlalchemy.orm import Session
from sqlalchemy import desc
from app.db.session import get_db
from app.models.arbitrage import ArbitrageOpportunity
from app.schemas.arbitrage import (
OpportunitiesList,
ArbitrageOpportunityCreate,
ArbitrageOpportunityUpdate,
ArbitrageOpportunity as ArbitrageOpportunitySchema
)
router = APIRouter()
@router.get("", response_model=OpportunitiesList)
async def get_arbitrage_opportunities(
viable_only: bool = Query(True, description="Show only viable opportunities that meet profit threshold"),
token_address: Optional[str] = Query(None, description="Filter by specific token address"),
min_profit_percent: Optional[float] = Query(None, description="Filter by minimum profit percentage"),
limit: int = Query(20, ge=1, le=100, description="Number of opportunities to return"),
offset: int = Query(0, ge=0, description="Pagination offset"),
db: Session = Depends(get_db)
) -> Any:
"""
Retrieve arbitrage opportunities with optional filtering.
"""
query = db.query(ArbitrageOpportunity)
# Apply filters
if viable_only:
query = query.filter(ArbitrageOpportunity.is_viable.is_(True))
if token_address:
query = query.filter(ArbitrageOpportunity.token_address == token_address)
if min_profit_percent is not None:
query = query.filter(ArbitrageOpportunity.price_difference_percent >= min_profit_percent)
# Get total count
total_count = query.count()
# Get paginated results
opportunities = query.order_by(desc(ArbitrageOpportunity.created_at)).offset(offset).limit(limit).all()
return {
"opportunities": opportunities,
"count": total_count,
"timestamp": datetime.utcnow()
}
@router.get("/{opportunity_id}", response_model=ArbitrageOpportunitySchema)
async def get_arbitrage_opportunity(
opportunity_id: int,
db: Session = Depends(get_db)
) -> Any:
"""
Get a specific arbitrage opportunity by ID.
"""
opportunity = db.query(ArbitrageOpportunity).filter(ArbitrageOpportunity.id == opportunity_id).first()
if not opportunity:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Arbitrage opportunity with ID {opportunity_id} not found"
)
return opportunity
@router.post("", response_model=ArbitrageOpportunitySchema, status_code=status.HTTP_201_CREATED)
async def create_arbitrage_opportunity(
opportunity_in: ArbitrageOpportunityCreate,
db: Session = Depends(get_db)
) -> Any:
"""
Create a new arbitrage opportunity.
"""
db_opportunity = ArbitrageOpportunity(
token_address=opportunity_in.token_address,
token_symbol=opportunity_in.token_symbol,
source_dex=opportunity_in.source_dex,
target_dex=opportunity_in.target_dex,
source_price=opportunity_in.source_price,
target_price=opportunity_in.target_price,
price_difference=opportunity_in.price_difference,
price_difference_percent=opportunity_in.price_difference_percent,
estimated_profit_usd=opportunity_in.estimated_profit_usd,
estimated_profit_token=opportunity_in.estimated_profit_token,
max_trade_amount_usd=opportunity_in.max_trade_amount_usd,
max_trade_amount_token=opportunity_in.max_trade_amount_token,
slippage_estimate=opportunity_in.slippage_estimate,
fees_estimate=opportunity_in.fees_estimate,
is_viable=opportunity_in.is_viable,
was_executed=False
)
db.add(db_opportunity)
db.commit()
db.refresh(db_opportunity)
return db_opportunity
@router.put("/{opportunity_id}", response_model=ArbitrageOpportunitySchema)
async def update_arbitrage_opportunity(
opportunity_id: int,
opportunity_update: ArbitrageOpportunityUpdate,
db: Session = Depends(get_db)
) -> Any:
"""
Update an existing arbitrage opportunity.
"""
db_opportunity = db.query(ArbitrageOpportunity).filter(ArbitrageOpportunity.id == opportunity_id).first()
if not db_opportunity:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Arbitrage opportunity with ID {opportunity_id} not found"
)
# Update fields if provided in the request
update_data = opportunity_update.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(db_opportunity, field, value)
db.commit()
db.refresh(db_opportunity)
return db_opportunity
@router.delete("/{opportunity_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
async def delete_arbitrage_opportunity(
opportunity_id: int,
db: Session = Depends(get_db)
) -> None:
"""
Delete an arbitrage opportunity.
"""
db_opportunity = db.query(ArbitrageOpportunity).filter(ArbitrageOpportunity.id == opportunity_id).first()
if not db_opportunity:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Arbitrage opportunity with ID {opportunity_id} not found"
)
db.delete(db_opportunity)
db.commit()
return None