Merge pull request '[Backend] Project Setup - FastAPI Structure and Dependencies' (#13) from fix/issue-2 into main
This commit was merged in pull request #13.
This commit is contained in:
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
.kugetsu/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
*.so
|
||||||
|
*.egg
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.env
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
*.db
|
||||||
|
data/
|
||||||
11
src/backend/.env.example
Normal file
11
src/backend/.env.example
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
DATABASE_URL=sqlite:///./data/app.db
|
||||||
|
SECRET_KEY=your-super-secret-key-change-in-production
|
||||||
|
JWT_ALGORITHM=HS256
|
||||||
|
ACCESS_TOKEN_EXPIRE_MINUTES=1440
|
||||||
|
MINIMAX_API_KEY=your-minimax-api-key
|
||||||
|
MINIMAX_MODEL=MiniMax-Text-01
|
||||||
|
AVE_API_KEY=your-ave-cloud-api-key
|
||||||
|
AVE_API_PLAN=free
|
||||||
|
HOST=0.0.0.0
|
||||||
|
PORT=8000
|
||||||
|
DEBUG=false
|
||||||
0
src/backend/app/api/__init__.py
Normal file
0
src/backend/app/api/__init__.py
Normal file
52
src/backend/app/api/auth.py
Normal file
52
src/backend/app/api/auth.py
Normal 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"
|
||||||
|
)
|
||||||
36
src/backend/app/api/backtest.py
Normal file
36
src/backend/app/api/backtest.py
Normal 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"
|
||||||
|
)
|
||||||
57
src/backend/app/api/bots.py
Normal file
57
src/backend/app/api/bots.py
Normal 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"
|
||||||
|
)
|
||||||
13
src/backend/app/api/config.py
Normal file
13
src/backend/app/api/config.py
Normal 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": []}
|
||||||
38
src/backend/app/api/simulate.py
Normal file
38
src/backend/app/api/simulate.py
Normal 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"
|
||||||
|
)
|
||||||
0
src/backend/app/core/__init__.py
Normal file
0
src/backend/app/core/__init__.py
Normal file
25
src/backend/app/core/config.py
Normal file
25
src/backend/app/core/config.py
Normal 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()
|
||||||
40
src/backend/app/core/database.py
Normal file
40
src/backend/app/core/database.py
Normal 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()
|
||||||
42
src/backend/app/core/security.py
Normal file
42
src/backend/app/core/security.py
Normal 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)
|
||||||
0
src/backend/app/db/__init__.py
Normal file
0
src/backend/app/db/__init__.py
Normal file
121
src/backend/app/db/models.py
Normal file
121
src/backend/app/db/models.py
Normal 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)
|
||||||
93
src/backend/app/db/schemas.py
Normal file
93
src/backend/app/db/schemas.py
Normal 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
33
src/backend/app/main.py
Normal 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"}
|
||||||
0
src/backend/app/services/__init__.py
Normal file
0
src/backend/app/services/__init__.py
Normal file
4
src/backend/app/services/ai_agent/__init__.py
Normal file
4
src/backend/app/services/ai_agent/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .crew import CrewAgent
|
||||||
|
from .llm_connector import LLMConnector
|
||||||
|
|
||||||
|
__all__ = ["CrewAgent", "LLMConnector"]
|
||||||
15
src/backend/app/services/ai_agent/crew.py
Normal file
15
src/backend/app/services/ai_agent/crew.py
Normal 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")
|
||||||
13
src/backend/app/services/ai_agent/llm_connector.py
Normal file
13
src/backend/app/services/ai_agent/llm_connector.py
Normal 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")
|
||||||
3
src/backend/app/services/backtest/__init__.py
Normal file
3
src/backend/app/services/backtest/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .engine import BacktestEngine
|
||||||
|
|
||||||
|
__all__ = ["BacktestEngine"]
|
||||||
15
src/backend/app/services/backtest/engine.py
Normal file
15
src/backend/app/services/backtest/engine.py
Normal 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")
|
||||||
3
src/backend/app/services/simulate/__init__.py
Normal file
3
src/backend/app/services/simulate/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .engine import SimulateEngine
|
||||||
|
|
||||||
|
__all__ = ["SimulateEngine"]
|
||||||
15
src/backend/app/services/simulate/engine.py
Normal file
15
src/backend/app/services/simulate/engine.py
Normal 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")
|
||||||
12
src/backend/requirements.txt
Normal file
12
src/backend/requirements.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fastapi>=0.109.0
|
||||||
|
uvicorn>=0.27.0
|
||||||
|
sqlalchemy>=2.0.0
|
||||||
|
pydantic>=2.5.0
|
||||||
|
pydantic-settings>=2.1.0
|
||||||
|
email-validator>=2.0.0
|
||||||
|
python-jose[cryptography]>=3.3.0
|
||||||
|
passlib[bcrypt]>=1.7.4
|
||||||
|
crewai>=0.1.0
|
||||||
|
anthropic>=0.18.0
|
||||||
|
httpx>=0.26.0
|
||||||
|
python-multipart>=0.0.6
|
||||||
11
src/backend/run.py
Normal file
11
src/backend/run.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import uvicorn
|
||||||
|
from app.core.config import get_settings
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
settings = get_settings()
|
||||||
|
uvicorn.run(
|
||||||
|
"app.main:app",
|
||||||
|
host=settings.HOST,
|
||||||
|
port=settings.PORT,
|
||||||
|
reload=settings.DEBUG,
|
||||||
|
)
|
||||||
20
src/backend/setup.py
Normal file
20
src/backend/setup.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="randebu-backend",
|
||||||
|
version="0.1.0",
|
||||||
|
packages=find_packages(),
|
||||||
|
install_requires=[
|
||||||
|
"fastapi>=0.109.0",
|
||||||
|
"uvicorn>=0.27.0",
|
||||||
|
"sqlalchemy>=2.0.0",
|
||||||
|
"pydantic>=2.5.0",
|
||||||
|
"pydantic-settings>=2.1.0",
|
||||||
|
"python-jose[cryptography]>=3.3.0",
|
||||||
|
"passlib[bcrypt]>=1.7.4",
|
||||||
|
"crewai>=0.1.0",
|
||||||
|
"anthropic>=0.18.0",
|
||||||
|
"httpx>=0.26.0",
|
||||||
|
"python-multipart>=0.0.6",
|
||||||
|
],
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user