feat: backend project setup with FastAPI structure and dependencies

- Create directory structure per IMPLEMENTATION_PLAN.md Section 12
- Add requirements.txt with FastAPI, SQLAlchemy, CrewAI, etc.
- Add core/config.py for environment variable configuration
- Add core/database.py for SQLite connection
- Add core/security.py for password hashing and JWT
- Add FastAPI app entry point (main.py) with all API routers
- Add Uvicorn runner (run.py)
- Add API route stubs (auth, bots, backtest, simulate, config)
- Add db/models.py with SQLAlchemy models
- Add db/schemas.py with Pydantic schemas
- Add service stubs (ai_agent, backtest, simulate engines)
- Add .env.example with all required environment variables
- Verify server starts correctly
This commit is contained in:
shokollm
2026-04-08 03:48:21 +00:00
parent 75a6273013
commit f2b5bd5f45
41 changed files with 684 additions and 0 deletions

Binary file not shown.

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,52 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from datetime import timedelta
from typing import Annotated
from ..core.database import get_db
from ..core.security import (
get_password_hash,
verify_password,
create_access_token,
verify_token,
)
from ..core.config import get_settings
from ..db.schemas import UserCreate, UserResponse, Token
router = APIRouter()
settings = get_settings()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
@router.post("/register", response_model=UserResponse)
def register(user: UserCreate, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.post("/login")
def login(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
db: Session = Depends(get_db),
):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.post("/logout")
def logout(token: Annotated[str, Depends(oauth2_scheme)]):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/me", response_model=UserResponse)
def get_me(
token: Annotated[str, Depends(oauth2_scheme)], db: Session = Depends(get_db)
):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)

View File

@@ -0,0 +1,36 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from ..core.database import get_db
from ..db.schemas import BacktestCreate, BacktestResponse
router = APIRouter()
@router.post("/bots/{bot_id}/backtest", response_model=BacktestResponse)
def start_backtest(bot_id: str, config: BacktestCreate, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/bots/{bot_id}/backtest/{run_id}", response_model=BacktestResponse)
def get_backtest(bot_id: str, run_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/bots/{bot_id}/backtests", response_model=List[BacktestResponse])
def list_backtests(bot_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.post("/bots/{bot_id}/backtest/{run_id}/stop")
def stop_backtest(bot_id: str, run_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)

View File

@@ -0,0 +1,57 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from ..core.database import get_db
from ..db.schemas import BotCreate, BotUpdate, BotResponse
router = APIRouter()
@router.get("", response_model=List[BotResponse])
def list_bots(db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.post("", response_model=BotResponse)
def create_bot(bot: BotCreate, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/{bot_id}", response_model=BotResponse)
def get_bot(bot_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.put("/{bot_id}", response_model=BotResponse)
def update_bot(bot_id: str, bot: BotUpdate, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.delete("/{bot_id}")
def delete_bot(bot_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.post("/{bot_id}/chat")
def chat(bot_id: str, message: dict, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/{bot_id}/history")
def get_history(bot_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)

View File

@@ -0,0 +1,13 @@
from fastapi import APIRouter
router = APIRouter()
@router.get("/chains")
def get_chains():
return {"chains": []}
@router.get("/tokens")
def get_tokens():
return {"tokens": []}

View File

@@ -0,0 +1,38 @@
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from ..core.database import get_db
from ..db.schemas import SimulationCreate, SimulationResponse
router = APIRouter()
@router.post("/bots/{bot_id}/simulate", response_model=SimulationResponse)
def start_simulation(
bot_id: str, config: SimulationCreate, db: Session = Depends(get_db)
):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/bots/{bot_id}/simulate/{run_id}", response_model=SimulationResponse)
def get_simulation(bot_id: str, run_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.get("/bots/{bot_id}/simulations", response_model=List[SimulationResponse])
def list_simulations(bot_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)
@router.post("/bots/{bot_id}/simulate/{run_id}/stop")
def stop_simulation(bot_id: str, run_id: str, db: Session = Depends(get_db)):
raise HTTPException(
status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented"
)

View File

View File

@@ -0,0 +1,25 @@
from pydantic_settings import BaseSettings
from functools import lru_cache
class Settings(BaseSettings):
DATABASE_URL: str = "sqlite:///./data/app.db"
SECRET_KEY: str
JWT_ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 1440
MINIMAX_API_KEY: str
MINIMAX_MODEL: str = "MiniMax-Text-01"
AVE_API_KEY: str
AVE_API_PLAN: str = "free"
HOST: str = "0.0.0.0"
PORT: int = 8000
DEBUG: bool = False
class Config:
env_file = ".env"
extra = "allow"
@lru_cache()
def get_settings() -> Settings:
return Settings()

View File

@@ -0,0 +1,40 @@
import os
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.engine import Engine
from .config import get_settings
settings = get_settings()
if settings.DATABASE_URL.startswith("sqlite"):
db_path = settings.DATABASE_URL.replace("sqlite:///", "")
os.makedirs(
os.path.dirname(db_path) if os.path.dirname(db_path) else ".", exist_ok=True
)
@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_conn, connection_record):
cursor = dbapi_conn.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
engine = create_engine(
settings.DATABASE_URL,
connect_args={"check_same_thread": False}
if settings.DATABASE_URL.startswith("sqlite")
else {},
echo=settings.DEBUG,
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

View File

@@ -0,0 +1,42 @@
from datetime import datetime, timedelta
from typing import Any, Optional
from jose import jwt, JWTError
from passlib.context import CryptContext
from .config import get_settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
to_encode = data.copy()
settings = get_settings()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(
to_encode, settings.SECRET_KEY, algorithm=settings.JWT_ALGORITHM
)
return encoded_jwt
def verify_token(token: str) -> Optional[dict[str, Any]]:
settings = get_settings()
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[settings.JWT_ALGORITHM]
)
return payload
except JWTError:
return None
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)

View File

Binary file not shown.

View File

@@ -0,0 +1,121 @@
import uuid
from datetime import datetime
from sqlalchemy import (
Column,
String,
Text,
Float,
Boolean,
DateTime,
ForeignKey,
Index,
JSON,
)
from sqlalchemy.orm import relationship
from ..core.database import Base
def generate_uuid():
return str(uuid.uuid4())
class User(Base):
__tablename__ = "users"
id = Column(String, primary_key=True, default=generate_uuid)
email = Column(String, unique=True, nullable=False)
password_hash = Column(String, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
bots = relationship("Bot", back_populates="user", cascade="all, delete-orphan")
class Bot(Base):
__tablename__ = "bots"
id = Column(String, primary_key=True, default=generate_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False)
name = Column(String, nullable=False)
description = Column(Text)
strategy_config = Column(JSON, nullable=False)
llm_config = Column(JSON, nullable=False)
status = Column(String, default="draft")
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
user = relationship("User", back_populates="bots")
conversations = relationship(
"BotConversation", back_populates="bot", cascade="all, delete-orphan"
)
backtests = relationship(
"Backtest", back_populates="bot", cascade="all, delete-orphan"
)
simulations = relationship(
"Simulation", back_populates="bot", cascade="all, delete-orphan"
)
signals = relationship("Signal", back_populates="bot", cascade="all, delete-orphan")
class BotConversation(Base):
__tablename__ = "bot_conversations"
id = Column(String, primary_key=True, default=generate_uuid)
bot_id = Column(String, ForeignKey("bots.id"), nullable=False)
role = Column(String, nullable=False)
content = Column(Text, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
bot = relationship("Bot", back_populates="conversations")
class Backtest(Base):
__tablename__ = "backtests"
id = Column(String, primary_key=True, default=generate_uuid)
bot_id = Column(String, ForeignKey("bots.id"), nullable=False)
started_at = Column(DateTime, nullable=False)
ended_at = Column(DateTime)
status = Column(String, nullable=False)
config = Column(JSON, nullable=False)
result = Column(JSON)
bot = relationship("Bot", back_populates="backtests")
class Simulation(Base):
__tablename__ = "simulations"
id = Column(String, primary_key=True, default=generate_uuid)
bot_id = Column(String, ForeignKey("bots.id"), nullable=False)
started_at = Column(DateTime, nullable=False)
status = Column(String, nullable=False)
config = Column(JSON, nullable=False)
signals = Column(JSON)
bot = relationship("Bot", back_populates="simulations")
class Signal(Base):
__tablename__ = "signals"
id = Column(String, primary_key=True, default=generate_uuid)
bot_id = Column(String, ForeignKey("bots.id"), nullable=False)
run_id = Column(String, nullable=False)
signal_type = Column(String, nullable=False)
token = Column(String, nullable=False)
price = Column(Float, nullable=False)
confidence = Column(Float)
reasoning = Column(Text)
executed = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
bot = relationship("Bot", back_populates="signals")
Index("idx_bots_user_id", Bot.user_id)
Index("idx_conversations_bot_id", BotConversation.bot_id)
Index("idx_backtests_bot_id", Backtest.bot_id)
Index("idx_simulations_bot_id", Simulation.bot_id)
Index("idx_signals_bot_id", Signal.bot_id)
Index("idx_signals_run_id", Signal.run_id)

View File

@@ -0,0 +1,93 @@
from pydantic import BaseModel, EmailStr
from typing import Optional, List, Any
from datetime import datetime
class UserCreate(BaseModel):
email: EmailStr
password: str
class UserResponse(BaseModel):
id: str
email: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class Token(BaseModel):
access_token: str
token_type: str
class BotCreate(BaseModel):
name: str
description: Optional[str] = None
strategy_config: dict
llm_config: dict
class BotUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
strategy_config: Optional[dict] = None
llm_config: Optional[dict] = None
status: Optional[str] = None
class BotResponse(BaseModel):
id: str
user_id: str
name: str
description: Optional[str]
strategy_config: dict
llm_config: dict
status: str
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class BacktestCreate(BaseModel):
token: str
chain: str
timeframe: str
start_date: str
end_date: str
class BacktestResponse(BaseModel):
id: str
bot_id: str
started_at: datetime
ended_at: Optional[datetime]
status: str
config: dict
result: Optional[dict]
class Config:
from_attributes = True
class SimulationCreate(BaseModel):
token: str
chain: str
duration_seconds: int = 3600
auto_execute: bool = False
class SimulationResponse(BaseModel):
id: str
bot_id: str
started_at: datetime
status: str
config: dict
signals: Optional[List[dict]]
class Config:
from_attributes = True

33
src/backend/app/main.py Normal file
View File

@@ -0,0 +1,33 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .api import auth, bots, backtest, simulate, config
app = FastAPI(
title="Randebu Trading Bot API",
description="AI-powered trading bot platform API",
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
app.include_router(bots.router, prefix="/api/bots", tags=["bots"])
app.include_router(backtest.router, prefix="/api", tags=["backtest"])
app.include_router(simulate.router, prefix="/api", tags=["simulate"])
app.include_router(config.router, prefix="/api/config", tags=["config"])
@app.get("/")
def root():
return {"status": "ok", "message": "Randebu Trading Bot API"}
@app.get("/health")
def health():
return {"status": "healthy"}

View File

View File

@@ -0,0 +1,4 @@
from .crew import CrewAgent
from .llm_connector import LLMConnector
__all__ = ["CrewAgent", "LLMConnector"]

View File

@@ -0,0 +1,15 @@
from typing import List, Optional
class CrewAgent:
def __init__(self, role: str, goal: str, backstory: str):
self.role = role
self.goal = goal
self.backstory = backstory
def execute_task(self, task: str) -> str:
raise NotImplementedError("CrewAI agent not yet implemented")
def get_trading_crew():
raise NotImplementedError("Trading crew not yet implemented")

View File

@@ -0,0 +1,13 @@
from typing import Optional
class LLMConnector:
def __init__(self, api_key: str, model: str = "MiniMax-Text-01"):
self.api_key = api_key
self.model = model
def chat(self, messages: list[dict], **kwargs):
raise NotImplementedError("LLM integration not yet implemented")
def parse_strategy(self, user_message: str) -> dict:
raise NotImplementedError("Strategy parsing not yet implemented")

View File

@@ -0,0 +1,3 @@
from .engine import BacktestEngine
__all__ = ["BacktestEngine"]

View File

@@ -0,0 +1,15 @@
from typing import Optional, Dict, Any
class BacktestEngine:
def __init__(self, config: Dict[str, Any]):
self.config = config
async def run(self) -> Dict[str, Any]:
raise NotImplementedError("Backtest engine not yet implemented")
async def stop(self):
raise NotImplementedError("Backtest stop not yet implemented")
def get_results(self) -> Dict[str, Any]:
raise NotImplementedError("Backtest results not yet implemented")

View File

@@ -0,0 +1,3 @@
from .engine import SimulateEngine
__all__ = ["SimulateEngine"]

View File

@@ -0,0 +1,15 @@
from typing import Optional, Dict, Any, List
class SimulateEngine:
def __init__(self, config: Dict[str, Any]):
self.config = config
async def run(self) -> List[Dict[str, Any]]:
raise NotImplementedError("Simulation engine not yet implemented")
async def stop(self):
raise NotImplementedError("Simulation stop not yet implemented")
def get_signals(self) -> List[Dict[str, Any]]:
raise NotImplementedError("Simulation signals not yet implemented")