From f2b5bd5f459453b2151f9300a6c223c0131deb2a Mon Sep 17 00:00:00 2001 From: shokollm <270575765+shokollm@users.noreply.github.com> Date: Wed, 8 Apr 2026 03:48:21 +0000 Subject: [PATCH] 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 --- .gitignore | 1 + src/backend/.env | 11 ++ src/backend/.env.example | 11 ++ .../app/__pycache__/main.cpython-314.pyc | Bin 0 -> 1691 bytes src/backend/app/api/__init__.py | 0 .../api/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 190 bytes .../app/api/__pycache__/auth.cpython-314.pyc | Bin 0 -> 3109 bytes .../api/__pycache__/backtest.cpython-314.pyc | Bin 0 -> 2634 bytes .../app/api/__pycache__/bots.cpython-314.pyc | Bin 0 -> 3773 bytes .../api/__pycache__/config.cpython-314.pyc | Bin 0 -> 650 bytes .../api/__pycache__/simulate.cpython-314.pyc | Bin 0 -> 2680 bytes src/backend/app/api/auth.py | 52 ++++++++ src/backend/app/api/backtest.py | 36 ++++++ src/backend/app/api/bots.py | 57 +++++++++ src/backend/app/api/config.py | 13 ++ src/backend/app/api/simulate.py | 38 ++++++ src/backend/app/core/__init__.py | 0 .../core/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 191 bytes .../core/__pycache__/config.cpython-314.pyc | Bin 0 -> 1918 bytes .../core/__pycache__/database.cpython-314.pyc | Bin 0 -> 2030 bytes .../core/__pycache__/security.cpython-314.pyc | Bin 0 -> 3011 bytes src/backend/app/core/config.py | 25 ++++ src/backend/app/core/database.py | 40 ++++++ src/backend/app/core/security.py | 42 ++++++ src/backend/app/db/__init__.py | 0 .../db/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 189 bytes .../db/__pycache__/schemas.cpython-314.pyc | Bin 0 -> 6585 bytes src/backend/app/db/models.py | 121 ++++++++++++++++++ src/backend/app/db/schemas.py | 93 ++++++++++++++ src/backend/app/main.py | 33 +++++ src/backend/app/services/__init__.py | 0 src/backend/app/services/ai_agent/__init__.py | 4 + src/backend/app/services/ai_agent/crew.py | 15 +++ .../app/services/ai_agent/llm_connector.py | 13 ++ src/backend/app/services/backtest/__init__.py | 3 + src/backend/app/services/backtest/engine.py | 15 +++ src/backend/app/services/simulate/__init__.py | 3 + src/backend/app/services/simulate/engine.py | 15 +++ src/backend/requirements.txt | 12 ++ src/backend/run.py | 11 ++ src/backend/setup.py | 20 +++ 41 files changed, 684 insertions(+) create mode 100644 .gitignore create mode 100644 src/backend/.env create mode 100644 src/backend/.env.example create mode 100644 src/backend/app/__pycache__/main.cpython-314.pyc create mode 100644 src/backend/app/api/__init__.py create mode 100644 src/backend/app/api/__pycache__/__init__.cpython-314.pyc create mode 100644 src/backend/app/api/__pycache__/auth.cpython-314.pyc create mode 100644 src/backend/app/api/__pycache__/backtest.cpython-314.pyc create mode 100644 src/backend/app/api/__pycache__/bots.cpython-314.pyc create mode 100644 src/backend/app/api/__pycache__/config.cpython-314.pyc create mode 100644 src/backend/app/api/__pycache__/simulate.cpython-314.pyc create mode 100644 src/backend/app/api/auth.py create mode 100644 src/backend/app/api/backtest.py create mode 100644 src/backend/app/api/bots.py create mode 100644 src/backend/app/api/config.py create mode 100644 src/backend/app/api/simulate.py create mode 100644 src/backend/app/core/__init__.py create mode 100644 src/backend/app/core/__pycache__/__init__.cpython-314.pyc create mode 100644 src/backend/app/core/__pycache__/config.cpython-314.pyc create mode 100644 src/backend/app/core/__pycache__/database.cpython-314.pyc create mode 100644 src/backend/app/core/__pycache__/security.cpython-314.pyc create mode 100644 src/backend/app/core/config.py create mode 100644 src/backend/app/core/database.py create mode 100644 src/backend/app/core/security.py create mode 100644 src/backend/app/db/__init__.py create mode 100644 src/backend/app/db/__pycache__/__init__.cpython-314.pyc create mode 100644 src/backend/app/db/__pycache__/schemas.cpython-314.pyc create mode 100644 src/backend/app/db/models.py create mode 100644 src/backend/app/db/schemas.py create mode 100644 src/backend/app/main.py create mode 100644 src/backend/app/services/__init__.py create mode 100644 src/backend/app/services/ai_agent/__init__.py create mode 100644 src/backend/app/services/ai_agent/crew.py create mode 100644 src/backend/app/services/ai_agent/llm_connector.py create mode 100644 src/backend/app/services/backtest/__init__.py create mode 100644 src/backend/app/services/backtest/engine.py create mode 100644 src/backend/app/services/simulate/__init__.py create mode 100644 src/backend/app/services/simulate/engine.py create mode 100644 src/backend/requirements.txt create mode 100644 src/backend/run.py create mode 100644 src/backend/setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c13afad --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.kugetsu/ diff --git a/src/backend/.env b/src/backend/.env new file mode 100644 index 0000000..4e31b39 --- /dev/null +++ b/src/backend/.env @@ -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 diff --git a/src/backend/.env.example b/src/backend/.env.example new file mode 100644 index 0000000..4e31b39 --- /dev/null +++ b/src/backend/.env.example @@ -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 diff --git a/src/backend/app/__pycache__/main.cpython-314.pyc b/src/backend/app/__pycache__/main.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eba21a80f69662cbb1845aa69fc4bcf92f67832e GIT binary patch literal 1691 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 literal 0 HcmV?d00001 diff --git a/src/backend/app/api/__pycache__/backtest.cpython-314.pyc b/src/backend/app/api/__pycache__/backtest.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b11ad531eda6a040e34ee9516681f390511dcdc GIT binary patch literal 2634 zcmcgtO>7%Q6rSC6){bL4e{K^JAb-@tD)APQkQSvtp`o^vA9snk5VKm_<75@vYi7nO zNrmJ}NF2Cv=GaRFhaR|;K`d_0WgVul%Fcx|)-N&n1Lv=mVrnDU_6$l$4pARG5u~0J1!eb~SO`zGPc^1h;(qpYd+oP^#!n%K6 zc2_d;1j+4>?=y1r9QAU_nXXU}fN#m8d>Ah2qf%+>N-;>AsXs>2{T2;`lG)3NGiV z6HczJr58WS;R>_tvZGRFvMP;Bj{jXuDJ;R%w`5TUqq>2ak#aJvxqA)rUFie=yq6B#k7JHx{jDo14--1IwM@N2&&ioji zsV^-5rN}*M1Ib+~ISuc(lgJ>1UY?b2mB0kVTOn?#(0e6XJF5 zW#sCf=1Pg2X84E3wD;TA5ENw5-^I^)YPTgt^Fs_3R$ zJ}$6cT0!@zlusMjxav*U20xNzm!{ zAR3A!N%zskJ#_INn!JaGen*q{(S={S2R<9EsSOp$flo(ojMl@k`uvsJ=$@3^e`oo- z!ngL{{QWpkkerelGE5sGt~zpKq#jDt-&n1U>`CeU3vb>zeS6GPy?R15c8k>)mhR3k obN!Y5#N4gImu=IlhxLUQvwPCq{%e=+#BOVz)mQ)I=Zot52fg&{-T(jq literal 0 HcmV?d00001 diff --git a/src/backend/app/api/__pycache__/bots.cpython-314.pyc b/src/backend/app/api/__pycache__/bots.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6a93722c6ef935dc131007a62a1cb8d5cbae319 GIT binary patch literal 3773 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 Z?qXtFnS3-ov-#e}t%ldspBPjM{craK`osVL literal 0 HcmV?d00001 diff --git a/src/backend/app/api/auth.py b/src/backend/app/api/auth.py new file mode 100644 index 0000000..afcc7eb --- /dev/null +++ b/src/backend/app/api/auth.py @@ -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" + ) diff --git a/src/backend/app/api/backtest.py b/src/backend/app/api/backtest.py new file mode 100644 index 0000000..e5f1a93 --- /dev/null +++ b/src/backend/app/api/backtest.py @@ -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" + ) diff --git a/src/backend/app/api/bots.py b/src/backend/app/api/bots.py new file mode 100644 index 0000000..4b60ca7 --- /dev/null +++ b/src/backend/app/api/bots.py @@ -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" + ) diff --git a/src/backend/app/api/config.py b/src/backend/app/api/config.py new file mode 100644 index 0000000..5b8613a --- /dev/null +++ b/src/backend/app/api/config.py @@ -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": []} diff --git a/src/backend/app/api/simulate.py b/src/backend/app/api/simulate.py new file mode 100644 index 0000000..734b8fa --- /dev/null +++ b/src/backend/app/api/simulate.py @@ -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" + ) diff --git a/src/backend/app/core/__init__.py b/src/backend/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/app/core/__pycache__/__init__.cpython-314.pyc b/src/backend/app/core/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77ad169b5adaca2467f46e1b5bb1f25f4dfed2bb GIT binary patch literal 191 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 literal 0 HcmV?d00001 diff --git a/src/backend/app/core/config.py b/src/backend/app/core/config.py new file mode 100644 index 0000000..2019928 --- /dev/null +++ b/src/backend/app/core/config.py @@ -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() diff --git a/src/backend/app/core/database.py b/src/backend/app/core/database.py new file mode 100644 index 0000000..31750fd --- /dev/null +++ b/src/backend/app/core/database.py @@ -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() diff --git a/src/backend/app/core/security.py b/src/backend/app/core/security.py new file mode 100644 index 0000000..fd76092 --- /dev/null +++ b/src/backend/app/core/security.py @@ -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) diff --git a/src/backend/app/db/__init__.py b/src/backend/app/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/app/db/__pycache__/__init__.cpython-314.pyc b/src/backend/app/db/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc80a0f34ad82e6c853bd8eced2e6e7e7df7fb6c GIT binary patch literal 189 zcmdPqKEC9=SF+czS literal 0 HcmV?d00001 diff --git a/src/backend/app/db/__pycache__/schemas.cpython-314.pyc b/src/backend/app/db/__pycache__/schemas.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70a2597c3296e13b5139fc3413cf32bcf4e213f1 GIT binary patch literal 6585 zcmb_gTW{RP6&^0R-1}8GuVUSt+RZw#wy+J3%!aM- ztZJ#kCeKA?BUWUB4Y4U!Rz_Gk{E>87cG|L{!m7d=@mXWS8ih6Hv&Mxr4r{_^O$cie z)|Ahh6xKAX8J{&JtXWudK5JT7^RN#1tQlb)gmuVg%?j%~++ zLhj4{8&E2k?vI!ym6;W~uUhgjGuT~rNh(VttP!=8a>xqbWtLi&(GA6p-!XOmHrLT^ z6|Q1*)1~01iQm5naaGzt8}~z3K4Yu~>uL6LX+Mt5+>eGwIVa8}_}@im|Gf z%oXE-QJQ|xTGlPIRYEflEUxQjX}NAqFD>$>IbAg_h*}qTrCHM#TNf^tOkOQ5R;my5 zX023NTPxKTOJ;Qi-7%-v*6pOGRhmu1!mu^X9_dcPw7&qTW@Z86zn`;D+20cbn>Tl5 zCQ1KF=Qii}!qE0n$eyx=rx81kRioBwkd7byx>aeoB^gays&mt7)SG(K zKyl9ZyQNmM3eylK0R5_0wTfl&`eMt{O{ZpOimG~}p=nPgo}&(u%Vu~Ul2bqmK|SA( zK%nQJv(FeG@=5~8Az#q7KLZg!PQpQsfH06#4qbLpW+?(qvOrU~tXL}06e)+{bJb4L zM3!}P&1jlIjC#1*AH|=12+dh?S#nV6B67gr#`N1pbE&?3n-Al&g{LY%g(oMMaB7IZ z0%2#CxUuS+Ej~s?^o28Qni^YmTH@cs3r(Ato!_&a(eXcJoNC{PwSNp;N686efZ{>K zfEy38FE$i6Ho`oK2JKM2##5xkt0Mkt7=j&dtx=4Vj#$Z{m`5R?XGs?hcf!N*_y9Ff z2uzs|Qd$DV<|POR!O_I#dk%sV>CNlAgy3D3%iyzW^;zuD;wG%bntn#{8F6|7K6-3YS zx>gmuVaFSdRab}aL^x4uVk@-L9POpy&L{((qOO`(ARJG{Q=4;+qjH5OuRb2zR?d21 zH9Wel6gpuAj}G>PRk43m{RoX87gax`IwVAQh!sRs{d4N<#{17Hti6DuI{lqLgOY&r zr@GDu0K&Lc+!5SS9~fd~*@}bm6Xh-#?g-901Pp)0e~tnJ5oHemyo5p|6+LAhxl&oE zr_2LOzcK+H0$ZQ|g-T2uKL2u{W`Hh>TUEeu9x$*9E>Wzm)`YwRP z!;_EVOC=u(1ndjt9+wZ~9(i9A!PJA(XnS%BXZ?E*1(&JtH4=hx56>k2j59y0ou;AmCNuAlk%Z`IH<=mkBr_vu z#zQ<+3?TmSWai4T$xQkhHZ#}?xGQh}g_k(xJHm^g;=zgZAUE1!C*=uOS1R?U9izSI z5~p=I4ua&CM#-xk#imPWoC4!$ihLzFt^iNcGHVj195{%aRqVg~*((yH1rH9nLF~|6 zPR!)7M+?&7LjT0+2L8ZF(Es=(2@%F*3i$7E#+rF4#>#qQe<9kMJ3w0_z3rToyMBQY z&e3>J!d+jW;!A*wP6a&f3iVlh4}cAIh@(92qHpA$gkH3btZa-YuRz~-T#Ezw^J2WT~Gq}wzRpmQWMU3ZB6;%vCy+c(L z!GSb!-j^ndvm~4?Ii~?CP6HxH4WsU9K=xLBwbj5;hS7Ak3-xT8v3L7Z*Z*OUO)H*2 z6OiHTvH1de&u>E5*;)%*19U~h$%N6YnRc?$vJ6fCM6cqA#oZm)a=mHU%A#R3j;5`# z-mwLUYaV~}ZM*vu*9i6plOA&oj3EE(*&i$vV|~-NMH&=&>$rUFS5$|D=ntKmhsCAIYMR_)2<#~(jSO>HYvdqi)?ve^m^ zf`sTsm(8*rHlt+DgE3WHDCTkOyk4s`ag@qs`kugSt2m~qHA|CiQe>V2<+%p=iv7zd<_lInWy str: + raise NotImplementedError("CrewAI agent not yet implemented") + + +def get_trading_crew(): + raise NotImplementedError("Trading crew not yet implemented") diff --git a/src/backend/app/services/ai_agent/llm_connector.py b/src/backend/app/services/ai_agent/llm_connector.py new file mode 100644 index 0000000..a2baba4 --- /dev/null +++ b/src/backend/app/services/ai_agent/llm_connector.py @@ -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") diff --git a/src/backend/app/services/backtest/__init__.py b/src/backend/app/services/backtest/__init__.py new file mode 100644 index 0000000..adbcdd2 --- /dev/null +++ b/src/backend/app/services/backtest/__init__.py @@ -0,0 +1,3 @@ +from .engine import BacktestEngine + +__all__ = ["BacktestEngine"] diff --git a/src/backend/app/services/backtest/engine.py b/src/backend/app/services/backtest/engine.py new file mode 100644 index 0000000..48a3796 --- /dev/null +++ b/src/backend/app/services/backtest/engine.py @@ -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") diff --git a/src/backend/app/services/simulate/__init__.py b/src/backend/app/services/simulate/__init__.py new file mode 100644 index 0000000..68f6571 --- /dev/null +++ b/src/backend/app/services/simulate/__init__.py @@ -0,0 +1,3 @@ +from .engine import SimulateEngine + +__all__ = ["SimulateEngine"] diff --git a/src/backend/app/services/simulate/engine.py b/src/backend/app/services/simulate/engine.py new file mode 100644 index 0000000..d8d7c2e --- /dev/null +++ b/src/backend/app/services/simulate/engine.py @@ -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") diff --git a/src/backend/requirements.txt b/src/backend/requirements.txt new file mode 100644 index 0000000..409bb67 --- /dev/null +++ b/src/backend/requirements.txt @@ -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 diff --git a/src/backend/run.py b/src/backend/run.py new file mode 100644 index 0000000..f6e2e80 --- /dev/null +++ b/src/backend/run.py @@ -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, + ) diff --git a/src/backend/setup.py b/src/backend/setup.py new file mode 100644 index 0000000..ee8e139 --- /dev/null +++ b/src/backend/setup.py @@ -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", + ], +)