Local changes: Updated model training, removed debug instrumentation, and configuration improvements
This commit is contained in:
359
backend/api/settings.py
Normal file
359
backend/api/settings.py
Normal file
@@ -0,0 +1,359 @@
|
||||
from typing import Dict, Any, Optional
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import select
|
||||
|
||||
from src.core.database import Exchange, get_database
|
||||
from src.core.config import get_config
|
||||
from src.security.key_manager import get_key_manager
|
||||
from src.trading.paper_trading import get_paper_trading
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class RiskSettings(BaseModel):
|
||||
max_drawdown_percent: float
|
||||
daily_loss_limit_percent: float
|
||||
position_size_percent: float
|
||||
|
||||
|
||||
class PaperTradingSettings(BaseModel):
|
||||
initial_capital: float
|
||||
fee_exchange: str = "coinbase" # Which exchange's fee model to use
|
||||
|
||||
|
||||
class LoggingSettings(BaseModel):
|
||||
level: str
|
||||
dir: str
|
||||
retention_days: int
|
||||
|
||||
|
||||
class GeneralSettings(BaseModel):
|
||||
timezone: str = "UTC"
|
||||
theme: str = "dark"
|
||||
currency: str = "USD"
|
||||
|
||||
|
||||
class ExchangeCreate(BaseModel):
|
||||
name: str
|
||||
api_key: Optional[str] = None
|
||||
api_secret: Optional[str] = None
|
||||
sandbox: bool = False
|
||||
read_only: bool = True
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class ExchangeUpdate(BaseModel):
|
||||
api_key: Optional[str] = None
|
||||
api_secret: Optional[str] = None
|
||||
sandbox: Optional[bool] = None
|
||||
read_only: Optional[bool] = None
|
||||
enabled: Optional[bool] = None
|
||||
|
||||
|
||||
@router.get("/risk")
|
||||
async def get_risk_settings():
|
||||
"""Get risk management settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
return {
|
||||
"max_drawdown_percent": config.get("risk.max_drawdown_percent", 20.0),
|
||||
"daily_loss_limit_percent": config.get("risk.daily_loss_limit_percent", 5.0),
|
||||
"position_size_percent": config.get("risk.position_size_percent", 2.0),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/risk")
|
||||
async def update_risk_settings(settings: RiskSettings):
|
||||
"""Update risk management settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
config.set("risk.max_drawdown_percent", settings.max_drawdown_percent)
|
||||
config.set("risk.daily_loss_limit_percent", settings.daily_loss_limit_percent)
|
||||
config.set("risk.position_size_percent", settings.position_size_percent)
|
||||
config.save()
|
||||
return {"status": "success"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/paper-trading")
|
||||
async def get_paper_trading_settings():
|
||||
"""Get paper trading settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
fee_exchange = config.get("paper_trading.fee_exchange", "coinbase")
|
||||
|
||||
# Get fee rates for current exchange
|
||||
fee_rates = config.get(f"trading.exchanges.{fee_exchange}.fees",
|
||||
config.get("trading.default_fees", {"maker": 0.001, "taker": 0.001}))
|
||||
|
||||
return {
|
||||
"initial_capital": config.get("paper_trading.default_capital", 100.0),
|
||||
"fee_exchange": fee_exchange,
|
||||
"fee_rates": fee_rates,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/paper-trading")
|
||||
async def update_paper_trading_settings(settings: PaperTradingSettings):
|
||||
"""Update paper trading settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
config.set("paper_trading.default_capital", settings.initial_capital)
|
||||
config.set("paper_trading.fee_exchange", settings.fee_exchange)
|
||||
config.save()
|
||||
return {"status": "success"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/paper-trading/fee-exchanges")
|
||||
async def get_available_fee_exchanges():
|
||||
"""Get available exchange fee models for paper trading."""
|
||||
try:
|
||||
config = get_config()
|
||||
exchanges_config = config.get("trading.exchanges", {})
|
||||
default_fees = config.get("trading.default_fees", {"maker": 0.001, "taker": 0.001})
|
||||
current = config.get("paper_trading.fee_exchange", "coinbase")
|
||||
|
||||
exchanges = [{"name": "default", "fees": default_fees}]
|
||||
for name, data in exchanges_config.items():
|
||||
if "fees" in data:
|
||||
exchanges.append({
|
||||
"name": name,
|
||||
"fees": data["fees"]
|
||||
})
|
||||
|
||||
return {
|
||||
"exchanges": exchanges,
|
||||
"current": current,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/paper-trading/reset")
|
||||
async def reset_paper_account():
|
||||
"""Reset paper trading account."""
|
||||
try:
|
||||
paper_trading = get_paper_trading()
|
||||
# Reset to capital from config
|
||||
success = await paper_trading.reset()
|
||||
if success:
|
||||
return {"status": "success", "message": "Paper account reset successfully"}
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail="Failed to reset paper account")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/logging")
|
||||
async def get_logging_settings():
|
||||
"""Get logging settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
return {
|
||||
"level": config.get("logging.level", "INFO"),
|
||||
"dir": config.get("logging.dir", ""),
|
||||
"retention_days": config.get("logging.retention_days", 30),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/logging")
|
||||
async def update_logging_settings(settings: LoggingSettings):
|
||||
"""Update logging settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
config.set("logging.level", settings.level)
|
||||
config.set("logging.dir", settings.dir)
|
||||
config.set("logging.retention_days", settings.retention_days)
|
||||
config.save()
|
||||
return {"status": "success"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/general")
|
||||
async def get_general_settings():
|
||||
"""Get general settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
return {
|
||||
"timezone": config.get("general.timezone", "UTC"),
|
||||
"theme": config.get("general.theme", "dark"),
|
||||
"currency": config.get("general.currency", "USD"),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/general")
|
||||
async def update_general_settings(settings: GeneralSettings):
|
||||
"""Update general settings."""
|
||||
try:
|
||||
config = get_config()
|
||||
config.set("general.timezone", settings.timezone)
|
||||
config.set("general.theme", settings.theme)
|
||||
config.set("general.currency", settings.currency)
|
||||
config.save()
|
||||
return {"status": "success"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/exchanges")
|
||||
async def create_exchange(exchange: ExchangeCreate):
|
||||
"""Create a new exchange."""
|
||||
try:
|
||||
db = get_database()
|
||||
async with db.get_session() as session:
|
||||
from src.exchanges.factory import ExchangeFactory
|
||||
from src.exchanges.public_data import PublicDataAdapter
|
||||
|
||||
# Check if this is a public data exchange
|
||||
adapter_class = None
|
||||
try:
|
||||
if hasattr(ExchangeFactory, '_adapters'):
|
||||
adapter_class = ExchangeFactory._adapters.get(exchange.name.lower())
|
||||
except:
|
||||
pass
|
||||
is_public_data = adapter_class == PublicDataAdapter if adapter_class else False
|
||||
|
||||
# Only require API keys for non-public-data exchanges
|
||||
if not is_public_data:
|
||||
if not exchange.api_key or not exchange.api_secret:
|
||||
raise HTTPException(status_code=400, detail="API key and secret are required")
|
||||
|
||||
new_exchange = Exchange(
|
||||
name=exchange.name,
|
||||
enabled=exchange.enabled
|
||||
)
|
||||
session.add(new_exchange)
|
||||
await session.flush()
|
||||
|
||||
# Save credentials
|
||||
key_manager = get_key_manager()
|
||||
key_manager.update_exchange(
|
||||
new_exchange.id,
|
||||
api_key=exchange.api_key or "",
|
||||
api_secret=exchange.api_secret or "",
|
||||
read_only=exchange.read_only if not is_public_data else True,
|
||||
sandbox=exchange.sandbox if not is_public_data else False
|
||||
)
|
||||
|
||||
await session.commit()
|
||||
return {"id": new_exchange.id, "name": new_exchange.name, "status": "created"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.put("/exchanges/{exchange_id}")
|
||||
async def update_exchange(exchange_id: int, exchange: ExchangeUpdate):
|
||||
"""Update an existing exchange."""
|
||||
try:
|
||||
db = get_database()
|
||||
async with db.get_session() as session:
|
||||
stmt = select(Exchange).where(Exchange.id == exchange_id)
|
||||
result = await session.execute(stmt)
|
||||
exchange_obj = result.scalar_one_or_none()
|
||||
if not exchange_obj:
|
||||
raise HTTPException(status_code=404, detail="Exchange not found")
|
||||
|
||||
key_manager = get_key_manager()
|
||||
credentials = key_manager.get_exchange_credentials(exchange_id)
|
||||
|
||||
# Update credentials if provided
|
||||
if exchange.api_key is not None or exchange.api_secret is not None:
|
||||
key_manager.update_exchange(
|
||||
exchange_id,
|
||||
api_key=exchange.api_key or credentials.get('api_key', ''),
|
||||
api_secret=exchange.api_secret or credentials.get('api_secret', ''),
|
||||
read_only=exchange.read_only if exchange.read_only is not None else credentials.get('read_only', True),
|
||||
sandbox=exchange.sandbox if exchange.sandbox is not None else credentials.get('sandbox', False)
|
||||
)
|
||||
|
||||
if exchange.enabled is not None:
|
||||
exchange_obj.enabled = exchange.enabled
|
||||
await session.commit()
|
||||
|
||||
return {"status": "success"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.delete("/exchanges/{exchange_id}")
|
||||
async def delete_exchange(exchange_id: int):
|
||||
"""Delete an exchange."""
|
||||
try:
|
||||
db = get_database()
|
||||
async with db.get_session() as session:
|
||||
stmt = select(Exchange).where(Exchange.id == exchange_id)
|
||||
result = await session.execute(stmt)
|
||||
exchange = result.scalar_one_or_none()
|
||||
if not exchange:
|
||||
raise HTTPException(status_code=404, detail="Exchange not found")
|
||||
|
||||
await session.delete(exchange)
|
||||
await session.commit()
|
||||
return {"status": "success"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/exchanges/{exchange_id}/test")
|
||||
async def test_exchange_connection(exchange_id: int):
|
||||
"""Test exchange connection."""
|
||||
try:
|
||||
db = get_database()
|
||||
async with db.get_session() as session:
|
||||
stmt = select(Exchange).where(Exchange.id == exchange_id)
|
||||
result = await session.execute(stmt)
|
||||
exchange = result.scalar_one_or_none()
|
||||
if not exchange:
|
||||
raise HTTPException(status_code=404, detail="Exchange not found")
|
||||
|
||||
from src.exchanges.factory import ExchangeFactory
|
||||
from src.exchanges.public_data import PublicDataAdapter
|
||||
|
||||
# Get adapter class safely
|
||||
adapter_class = None
|
||||
try:
|
||||
if hasattr(ExchangeFactory, '_adapters'):
|
||||
adapter_class = ExchangeFactory._adapters.get(exchange.name.lower())
|
||||
except:
|
||||
pass
|
||||
is_public_data = adapter_class == PublicDataAdapter if adapter_class else False
|
||||
|
||||
key_manager = get_key_manager()
|
||||
if not is_public_data and not key_manager.get_exchange_credentials(exchange_id):
|
||||
raise HTTPException(status_code=400, detail="No credentials found for this exchange")
|
||||
|
||||
adapter = ExchangeFactory.create(exchange_id)
|
||||
if adapter and adapter.connect():
|
||||
try:
|
||||
if is_public_data:
|
||||
adapter.get_ticker("BTC/USDT")
|
||||
else:
|
||||
adapter.get_balance()
|
||||
return {"status": "success", "message": "Connection successful"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Connected but failed to fetch data: {str(e)}"}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to connect"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
Reference in New Issue
Block a user