-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathorders.py
More file actions
74 lines (62 loc) · 2.46 KB
/
Copy pathorders.py
File metadata and controls
74 lines (62 loc) · 2.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"""Orders API — operator-placed manual orders.
The trading engine places orders automatically from strategy signals. This
endpoint lets an admin place a one-off order by hand — to buy and hold a coin,
or to trim a position — outside any strategy.
The placement logic itself lives in `trading.manual_order`; this module only
wires HTTP to it: authenticate, call the service, map its typed errors to HTTP
status codes, and record the audit entry.
"""
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status
from api.market import get_venue_router
from auth.audit import record_audit
from auth.deps import SessionDep, require_admin
from auth.models import User
from trading.executor_router import ExecutorRouter
from trading.executors.base import ExecutionError, Fill
from trading.manual_order import ManualOrderRequest, OrderBlocked, submit_manual_order
from trading.risk import RiskManager
from trading.venue_router import VenueRouter
from venues.base import VenueError
router = APIRouter(prefix="/api/orders", tags=["orders"])
AdminUser = Annotated[User, Depends(require_admin)]
VenueRouterDep = Annotated[VenueRouter, Depends(get_venue_router)]
def _executor_router() -> ExecutorRouter:
"""Overridable in tests; a fresh router resolves Sim/Testnet/Live per call."""
return ExecutorRouter()
@router.post("/manual", response_model=Fill)
def place_manual_order(
body: ManualOrderRequest,
admin: AdminUser,
session: SessionDep,
venues: VenueRouterDep,
) -> Fill:
"""Place a one-off order, risk-checked and recorded as `manual`."""
try:
result = submit_manual_order(
session,
body,
venues=venues,
executor_router=_executor_router(),
risk=RiskManager.from_store(session),
)
except VenueError as exc:
raise HTTPException(
status.HTTP_502_BAD_GATEWAY, f"could not price {body.symbol}: {exc}"
) from exc
except OrderBlocked as exc:
raise HTTPException(status.HTTP_409_CONFLICT, str(exc)) from exc
except ExecutionError as exc:
raise HTTPException(status.HTTP_400_BAD_REQUEST, str(exc)) from exc
record_audit(
session,
actor=admin.username,
action="order.manual",
detail={
"symbol": body.symbol,
"side": body.side.value,
"quantity": str(result.executed_quantity),
"mode": result.mode,
},
)
return result.fill