Add CRUD endpoints for opportunities, trades, and system events
- Add GET endpoint for individual arbitrage opportunities - Add POST endpoint for creating arbitrage opportunities - Add GET endpoint for individual trades - Add POST endpoint for creating trades - Add endpoints for system events (GET list, GET by ID, POST) - Update API router to include the new events endpoints - Fix linting issues
This commit is contained in:
parent
572a7c0dd0
commit
b7f18dcaec
@ -1,8 +1,9 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.api_v1.endpoints import status, opportunities, trades
|
from app.api.api_v1.endpoints import status, opportunities, trades, events
|
||||||
|
|
||||||
api_router = APIRouter(prefix="/api/v1")
|
api_router = APIRouter(prefix="/api/v1")
|
||||||
api_router.include_router(status.router, prefix="/status", tags=["status"])
|
api_router.include_router(status.router, prefix="/status", tags=["status"])
|
||||||
api_router.include_router(opportunities.router, prefix="/opportunities", tags=["opportunities"])
|
api_router.include_router(opportunities.router, prefix="/opportunities", tags=["opportunities"])
|
||||||
api_router.include_router(trades.router, prefix="/trades", tags=["trades"])
|
api_router.include_router(trades.router, prefix="/trades", tags=["trades"])
|
||||||
|
api_router.include_router(events.router, prefix="/events", tags=["events"])
|
84
app/api/api_v1/endpoints/events.py
Normal file
84
app/api/api_v1/endpoints/events.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from typing import Any, Optional, List
|
||||||
|
from fastapi import APIRouter, Depends, Query, HTTPException, status
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import desc
|
||||||
|
|
||||||
|
from app.db.session import get_db
|
||||||
|
from app.models.arbitrage import SystemEvent
|
||||||
|
from app.schemas.arbitrage import SystemEventCreate, SystemEvent as SystemEventSchema
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", response_model=List[SystemEventSchema])
|
||||||
|
async def get_system_events(
|
||||||
|
event_type: Optional[str] = Query(None, description="Filter by event type (startup, shutdown, error, warning, info)"),
|
||||||
|
component: Optional[str] = Query(None, description="Filter by component"),
|
||||||
|
limit: int = Query(50, ge=1, le=200, description="Number of events to return"),
|
||||||
|
offset: int = Query(0, ge=0, description="Pagination offset"),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Retrieve system events with optional filtering.
|
||||||
|
"""
|
||||||
|
query = db.query(SystemEvent)
|
||||||
|
|
||||||
|
# Apply filters
|
||||||
|
if event_type:
|
||||||
|
query = query.filter(SystemEvent.event_type == event_type)
|
||||||
|
|
||||||
|
if component:
|
||||||
|
query = query.filter(SystemEvent.component == component)
|
||||||
|
|
||||||
|
# Get paginated results
|
||||||
|
events = query.order_by(desc(SystemEvent.timestamp)).offset(offset).limit(limit).all()
|
||||||
|
|
||||||
|
return events
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{event_id}", response_model=SystemEventSchema)
|
||||||
|
async def get_system_event(
|
||||||
|
event_id: int,
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Get a specific system event by ID.
|
||||||
|
"""
|
||||||
|
event = db.query(SystemEvent).filter(SystemEvent.id == event_id).first()
|
||||||
|
if not event:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"System event with ID {event_id} not found"
|
||||||
|
)
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", response_model=SystemEventSchema, status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_system_event(
|
||||||
|
event_in: SystemEventCreate,
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Create a new system event.
|
||||||
|
"""
|
||||||
|
# Validate event type
|
||||||
|
valid_event_types = ["startup", "shutdown", "error", "warning", "info"]
|
||||||
|
if event_in.event_type not in valid_event_types:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail=f"Invalid event type. Must be one of: {', '.join(valid_event_types)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the system event
|
||||||
|
db_event = SystemEvent(
|
||||||
|
event_type=event_in.event_type,
|
||||||
|
component=event_in.component,
|
||||||
|
message=event_in.message,
|
||||||
|
details=event_in.details
|
||||||
|
)
|
||||||
|
|
||||||
|
db.add(db_event)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_event)
|
||||||
|
|
||||||
|
return db_event
|
@ -1,12 +1,12 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
|
||||||
from app.db.session import get_db
|
from app.db.session import get_db
|
||||||
from app.models.arbitrage import ArbitrageOpportunity
|
from app.models.arbitrage import ArbitrageOpportunity
|
||||||
from app.schemas.arbitrage import OpportunitiesList
|
from app.schemas.arbitrage import OpportunitiesList, ArbitrageOpportunityCreate, ArbitrageOpportunity as ArbitrageOpportunitySchema
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -45,4 +45,53 @@ async def get_arbitrage_opportunities(
|
|||||||
"opportunities": opportunities,
|
"opportunities": opportunities,
|
||||||
"count": total_count,
|
"count": total_count,
|
||||||
"timestamp": datetime.utcnow()
|
"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
|
@ -1,12 +1,12 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from fastapi import APIRouter, Depends, Query
|
from fastapi import APIRouter, Depends, Query, HTTPException, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy import desc, func
|
from sqlalchemy import desc, func
|
||||||
|
|
||||||
from app.db.session import get_db
|
from app.db.session import get_db
|
||||||
from app.models.arbitrage import Trade
|
from app.models.arbitrage import Trade, ArbitrageOpportunity
|
||||||
from app.schemas.arbitrage import TradesList
|
from app.schemas.arbitrage import TradesList, TradeCreate, Trade as TradeSchema
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -53,4 +53,70 @@ async def get_trades(
|
|||||||
"count": total_count,
|
"count": total_count,
|
||||||
"timestamp": datetime.utcnow(),
|
"timestamp": datetime.utcnow(),
|
||||||
"total_profit_usd": total_profit
|
"total_profit_usd": total_profit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{trade_id}", response_model=TradeSchema)
|
||||||
|
async def get_trade(
|
||||||
|
trade_id: int,
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Get a specific trade by ID.
|
||||||
|
"""
|
||||||
|
trade = db.query(Trade).filter(Trade.id == trade_id).first()
|
||||||
|
if not trade:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Trade with ID {trade_id} not found"
|
||||||
|
)
|
||||||
|
return trade
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", response_model=TradeSchema, status_code=status.HTTP_201_CREATED)
|
||||||
|
async def create_trade(
|
||||||
|
trade_in: TradeCreate,
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Create a new trade record.
|
||||||
|
"""
|
||||||
|
# Check if the opportunity exists
|
||||||
|
opportunity = db.query(ArbitrageOpportunity).filter(
|
||||||
|
ArbitrageOpportunity.id == trade_in.opportunity_id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if not opportunity:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Arbitrage opportunity with ID {trade_in.opportunity_id} not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the trade
|
||||||
|
db_trade = Trade(
|
||||||
|
opportunity_id=trade_in.opportunity_id,
|
||||||
|
token_address=trade_in.token_address,
|
||||||
|
token_symbol=trade_in.token_symbol,
|
||||||
|
source_dex=trade_in.source_dex,
|
||||||
|
target_dex=trade_in.target_dex,
|
||||||
|
input_amount=trade_in.input_amount,
|
||||||
|
input_amount_usd=trade_in.input_amount_usd,
|
||||||
|
output_amount=trade_in.output_amount,
|
||||||
|
output_amount_usd=trade_in.output_amount_usd,
|
||||||
|
profit_amount=trade_in.profit_amount,
|
||||||
|
profit_amount_usd=trade_in.profit_amount_usd,
|
||||||
|
profit_percent=trade_in.profit_percent,
|
||||||
|
tx_signature=trade_in.tx_signature,
|
||||||
|
tx_status=trade_in.tx_status,
|
||||||
|
tx_error=trade_in.tx_error
|
||||||
|
)
|
||||||
|
|
||||||
|
db.add(db_trade)
|
||||||
|
|
||||||
|
# Mark the opportunity as executed
|
||||||
|
opportunity.was_executed = True
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_trade)
|
||||||
|
|
||||||
|
return db_trade
|
@ -3,6 +3,7 @@ import logging
|
|||||||
from typing import Dict, List, Optional, Any, Tuple
|
from typing import Dict, List, Optional, Any, Tuple
|
||||||
import base64
|
import base64
|
||||||
import base58
|
import base58
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -54,8 +55,6 @@ except ImportError:
|
|||||||
class TxOpts:
|
class TxOpts:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from app.core.config import settings
|
|
||||||
|
|
||||||
# Initialize Solana client
|
# Initialize Solana client
|
||||||
solana_client = Client(settings.SOLANA_RPC_URL)
|
solana_client = Client(settings.SOLANA_RPC_URL)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user