148 lines
5.2 KiB
Python
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 |