167 lines
5.9 KiB
Python
167 lines
5.9 KiB
Python
|
|
"""Authenticated adapters; every mutation is committed before the execution lane wakes."""
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, Query, Request
|
||
|
|
|
||
|
|
from ..business import Business
|
||
|
|
from ..security import require_auth
|
||
|
|
from .contracts import (
|
||
|
|
ControlInput,
|
||
|
|
DraftInput,
|
||
|
|
DraftUpdate,
|
||
|
|
EventPage,
|
||
|
|
PreviewInput,
|
||
|
|
PreviewOutput,
|
||
|
|
ReferenceInput,
|
||
|
|
RerunInput,
|
||
|
|
ResultPage,
|
||
|
|
RunOutput,
|
||
|
|
RunPage,
|
||
|
|
SchedulerInput,
|
||
|
|
SchedulerOutput,
|
||
|
|
StartInput,
|
||
|
|
SubsetInput,
|
||
|
|
)
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/api/v1/backtests", tags=["backtests"], dependencies=[Depends(require_auth)])
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/capabilities")
|
||
|
|
async def capabilities(request: Request):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.capabilities()
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/config", response_model=SchedulerOutput)
|
||
|
|
async def config(request: Request):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.config()
|
||
|
|
|
||
|
|
|
||
|
|
@router.put("/config", response_model=SchedulerOutput)
|
||
|
|
async def configure(body: SchedulerInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
result = await Business(db).backtests.configure(body)
|
||
|
|
request.app.state.runner.backtests.wake.set()
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/drafts")
|
||
|
|
async def drafts(request: Request, limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.drafts(limit, offset)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/drafts", status_code=201)
|
||
|
|
async def save_draft(body: DraftInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
return await Business(db).backtests.save_draft(body)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/drafts/{draft_id}")
|
||
|
|
async def draft(draft_id: str, request: Request):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.draft(draft_id)
|
||
|
|
|
||
|
|
|
||
|
|
@router.put("/drafts/{draft_id}")
|
||
|
|
async def update_draft(draft_id: str, body: DraftUpdate, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
return await Business(db).backtests.save_draft(body, draft_id)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/previews", status_code=201, response_model=PreviewOutput)
|
||
|
|
async def preview(body: PreviewInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
return await Business(db).backtests.preview(body)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/previews/{preview_id}", response_model=PreviewOutput)
|
||
|
|
async def get_preview(
|
||
|
|
preview_id: str, request: Request, limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)
|
||
|
|
):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.get_preview(preview_id, limit, offset)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/runs", status_code=202, response_model=RunOutput)
|
||
|
|
async def start(body: StartInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
result = await Business(db).backtests.start(body)
|
||
|
|
request.app.state.runner.backtests.wake.set()
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/runs", response_model=RunPage)
|
||
|
|
async def runs(
|
||
|
|
request: Request,
|
||
|
|
limit: int = Query(25, ge=1, le=100),
|
||
|
|
offset: int = Query(0, ge=0),
|
||
|
|
source: str | None = Query(None, max_length=100),
|
||
|
|
):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.runs(limit, offset, source)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/runs/{run_id}", response_model=RunOutput)
|
||
|
|
async def run(run_id: str, request: Request):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.run(run_id)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/runs/{run_id}/results", response_model=ResultPage)
|
||
|
|
async def results(
|
||
|
|
run_id: str, request: Request, limit: int = Query(25, ge=1, le=100), offset: int = Query(0, ge=0)
|
||
|
|
):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.results(run_id, limit, offset)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/runs/{run_id}/events", response_model=EventPage)
|
||
|
|
async def events(
|
||
|
|
run_id: str, request: Request, after: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=100)
|
||
|
|
):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.events(run_id, after, limit)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/runs/{run_id}/attempts")
|
||
|
|
async def attempts(run_id: str, request: Request):
|
||
|
|
async with request.app.state.sessions() as db:
|
||
|
|
return await Business(db).backtests.attempts(run_id)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/runs/{run_id}/control", response_model=RunOutput)
|
||
|
|
async def control(run_id: str, body: ControlInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
result = await Business(db).backtests.control(run_id, body)
|
||
|
|
request.app.state.runner.backtests.wake.set()
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/runs/{run_id}/rerun-preview", status_code=201, response_model=PreviewOutput)
|
||
|
|
async def rerun(run_id: str, body: RerunInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
return await Business(db).backtests.rerun(run_id, body)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/attempts/{attempt_id}/reference", response_model=RunOutput)
|
||
|
|
async def attach_reference(attempt_id: str, body: ReferenceInput, request: Request):
|
||
|
|
from fastapi import HTTPException
|
||
|
|
|
||
|
|
from ..worldquant import WqError
|
||
|
|
|
||
|
|
try:
|
||
|
|
body.progress_url = request.app.state.runner.client.simulation_url(body.progress_url)
|
||
|
|
except WqError as exc:
|
||
|
|
raise HTTPException(422, str(exc)) from None
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
result = await Business(db).backtests.attach_reference(attempt_id, body)
|
||
|
|
request.app.state.runner.backtests.wake.set()
|
||
|
|
return result
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/previews/{preview_id}/subset", status_code=201, response_model=PreviewOutput)
|
||
|
|
async def subset(preview_id: str, body: SubsetInput, request: Request):
|
||
|
|
async with request.app.state.sessions.begin() as db:
|
||
|
|
return await Business(db).backtests.subset(preview_id, body)
|