feat(V2): migrazione deribit (client, leverage_cap, tools)
Task 6.1 V2.0.0: copia client.py + leverage_cap.py da services/mcp-deribit con import riscritti (mcp_common -> cerbero_mcp.common, mcp_deribit -> cerbero_mcp.exchanges.deribit). Estratte 34 tool async (28 endpoint + is_testnet/environment_info + helpers) in tools.py: pure logica senza FastAPI/ACL. Audit calls per ora rimossi (TODO: cabling via router su request.state.environment). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
|||||||
|
"""Leverage cap server-side per place_order.
|
||||||
|
|
||||||
|
Cap letto dal secret JSON via campo `max_leverage`. Default 1 (cash) se assente.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
|
||||||
|
def get_max_leverage(creds: dict) -> int:
|
||||||
|
"""Legge max_leverage dal secret. Default 1 se mancante."""
|
||||||
|
raw = creds.get("max_leverage", 1)
|
||||||
|
try:
|
||||||
|
value = int(raw)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
value = 1
|
||||||
|
return max(1, value)
|
||||||
|
|
||||||
|
|
||||||
|
def enforce_leverage(
|
||||||
|
requested: int | float | None,
|
||||||
|
*,
|
||||||
|
creds: dict,
|
||||||
|
exchange: str,
|
||||||
|
) -> int:
|
||||||
|
"""Verifica e applica leverage cap. Ritorna leverage applicabile.
|
||||||
|
|
||||||
|
Solleva HTTPException(403, LEVERAGE_CAP_EXCEEDED) se requested > cap.
|
||||||
|
Se requested is None, applica il cap come default.
|
||||||
|
"""
|
||||||
|
cap = get_max_leverage(creds)
|
||||||
|
if requested is None:
|
||||||
|
return cap
|
||||||
|
lev = int(requested)
|
||||||
|
if lev < 1:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail={
|
||||||
|
"error": "LEVERAGE_CAP_EXCEEDED",
|
||||||
|
"exchange": exchange,
|
||||||
|
"requested": lev,
|
||||||
|
"max": cap,
|
||||||
|
"reason": "leverage must be >= 1",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if lev > cap:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail={
|
||||||
|
"error": "LEVERAGE_CAP_EXCEEDED",
|
||||||
|
"exchange": exchange,
|
||||||
|
"requested": lev,
|
||||||
|
"max": cap,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return lev
|
||||||
@@ -0,0 +1,528 @@
|
|||||||
|
"""Tool deribit V2: pydantic schemas + async functions.
|
||||||
|
|
||||||
|
Ogni funzione prende (client: DeribitClient, params: <Req>) e restituisce
|
||||||
|
un dict (o un model Pydantic). Pure logica, no FastAPI dependency, no ACL.
|
||||||
|
L'autenticazione bearer è gestita dal middleware in cerbero_mcp.auth;
|
||||||
|
l'audit verrà cablato dal router via request.state.environment.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel, field_validator, model_validator
|
||||||
|
|
||||||
|
from cerbero_mcp.exchanges.deribit.client import DeribitClient
|
||||||
|
from cerbero_mcp.exchanges.deribit.leverage_cap import (
|
||||||
|
enforce_leverage as _enforce_leverage,
|
||||||
|
)
|
||||||
|
from cerbero_mcp.exchanges.deribit.leverage_cap import get_max_leverage
|
||||||
|
|
||||||
|
# === Schemas ===
|
||||||
|
|
||||||
|
|
||||||
|
class GetTickerReq(BaseModel):
|
||||||
|
instrument_name: str | None = None
|
||||||
|
instrument: str | None = None
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def _normalize(self):
|
||||||
|
sym = self.instrument_name or self.instrument
|
||||||
|
if not sym:
|
||||||
|
raise ValueError("instrument_name (or instrument) is required")
|
||||||
|
self.instrument_name = sym
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class GetTickerBatchReq(BaseModel):
|
||||||
|
instrument_names: list[str] | None = None
|
||||||
|
instruments: list[str] | None = None
|
||||||
|
|
||||||
|
model_config = {"extra": "allow"}
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def _normalize(self):
|
||||||
|
names = self.instrument_names or self.instruments
|
||||||
|
if not names:
|
||||||
|
raise ValueError("instrument_names (or instruments) is required")
|
||||||
|
self.instrument_names = names
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class GetInstrumentsReq(BaseModel):
|
||||||
|
currency: str
|
||||||
|
kind: str | None = None
|
||||||
|
expiry_from: str | None = None
|
||||||
|
expiry_to: str | None = None
|
||||||
|
strike_min: float | None = None
|
||||||
|
strike_max: float | None = None
|
||||||
|
min_open_interest: float | None = None
|
||||||
|
limit: int = 100
|
||||||
|
offset: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class GetOrderbookReq(BaseModel):
|
||||||
|
instrument_name: str
|
||||||
|
depth: int = 10
|
||||||
|
|
||||||
|
|
||||||
|
class OrderbookImbalanceReq(BaseModel):
|
||||||
|
instrument_name: str
|
||||||
|
depth: int = 10
|
||||||
|
|
||||||
|
|
||||||
|
class GetPositionsReq(BaseModel):
|
||||||
|
currency: str = "USDC"
|
||||||
|
|
||||||
|
|
||||||
|
class GetAccountSummaryReq(BaseModel):
|
||||||
|
currency: str = "USDC"
|
||||||
|
|
||||||
|
|
||||||
|
class GetTradeHistoryReq(BaseModel):
|
||||||
|
limit: int = 100
|
||||||
|
instrument_name: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class GetHistoricalReq(BaseModel):
|
||||||
|
instrument: str
|
||||||
|
start_date: str
|
||||||
|
end_date: str
|
||||||
|
resolution: str = "1h"
|
||||||
|
|
||||||
|
|
||||||
|
class GetDvolReq(BaseModel):
|
||||||
|
currency: str = "BTC"
|
||||||
|
start_date: str
|
||||||
|
end_date: str
|
||||||
|
resolution: str = "1D"
|
||||||
|
|
||||||
|
|
||||||
|
class GetDvolHistoryReq(BaseModel):
|
||||||
|
currency: str = "BTC"
|
||||||
|
lookback_days: int = 90
|
||||||
|
|
||||||
|
|
||||||
|
class GetIvRankReq(BaseModel):
|
||||||
|
instrument: str
|
||||||
|
|
||||||
|
|
||||||
|
class GetRealizedVolReq(BaseModel):
|
||||||
|
currency: str = "BTC"
|
||||||
|
windows: list[int] = [14, 30]
|
||||||
|
|
||||||
|
|
||||||
|
class GetGexReq(BaseModel):
|
||||||
|
currency: str
|
||||||
|
expiry_from: str | None = None
|
||||||
|
expiry_to: str | None = None
|
||||||
|
top_n_strikes: int = 50
|
||||||
|
|
||||||
|
|
||||||
|
class OptionFlowReq(BaseModel):
|
||||||
|
"""Body comune per indicatori option-flow (dealer gamma, vanna/charm,
|
||||||
|
OI-weighted skew, smile asymmetry, ATM vs wings)."""
|
||||||
|
|
||||||
|
currency: str
|
||||||
|
expiry_from: str | None = None
|
||||||
|
expiry_to: str | None = None
|
||||||
|
top_n_strikes: int = 100
|
||||||
|
|
||||||
|
|
||||||
|
class GetPcRatioReq(BaseModel):
|
||||||
|
currency: str
|
||||||
|
|
||||||
|
|
||||||
|
class GetSkew25dReq(BaseModel):
|
||||||
|
currency: str
|
||||||
|
expiry: str
|
||||||
|
|
||||||
|
|
||||||
|
class GetTermStructureReq(BaseModel):
|
||||||
|
currency: str
|
||||||
|
|
||||||
|
|
||||||
|
class CalculateSpreadPayoffReq(BaseModel):
|
||||||
|
legs: list[dict]
|
||||||
|
quote_currency: str = "USD"
|
||||||
|
|
||||||
|
|
||||||
|
class RunBacktestReq(BaseModel):
|
||||||
|
strategy_name: str
|
||||||
|
underlying: str = "BTC"
|
||||||
|
lookback_days: int = 30
|
||||||
|
resolution: str = "4h"
|
||||||
|
entry_rules: dict | None = None
|
||||||
|
exit_rules: dict | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class FindByDeltaReq(BaseModel):
|
||||||
|
currency: str
|
||||||
|
expiry: str
|
||||||
|
target_delta: float
|
||||||
|
option_type: str
|
||||||
|
max_results: int = 3
|
||||||
|
min_open_interest: float = 100.0
|
||||||
|
min_volume_24h: float = 20.0
|
||||||
|
|
||||||
|
|
||||||
|
class GetIndicatorsReq(BaseModel):
|
||||||
|
instrument: str
|
||||||
|
indicators: list[str]
|
||||||
|
start_date: str
|
||||||
|
end_date: str
|
||||||
|
resolution: str = "1h"
|
||||||
|
|
||||||
|
@field_validator("indicators", mode="before")
|
||||||
|
@classmethod
|
||||||
|
def _coerce_indicators(cls, v):
|
||||||
|
if isinstance(v, str):
|
||||||
|
import json
|
||||||
|
|
||||||
|
s = v.strip()
|
||||||
|
if s.startswith("["):
|
||||||
|
try:
|
||||||
|
parsed = json.loads(s)
|
||||||
|
if isinstance(parsed, list):
|
||||||
|
return [str(x).strip() for x in parsed if str(x).strip()]
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
return [x.strip() for x in s.split(",") if x.strip()]
|
||||||
|
if isinstance(v, list):
|
||||||
|
return v
|
||||||
|
raise ValueError(
|
||||||
|
"indicators must be a list like ['rsi','atr','macd'] "
|
||||||
|
"or a comma-separated string like 'rsi,atr,macd'"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PlaceOrderReq(BaseModel):
|
||||||
|
instrument_name: str
|
||||||
|
side: str # "buy" | "sell"
|
||||||
|
amount: float
|
||||||
|
type: str = "limit"
|
||||||
|
price: float | None = None
|
||||||
|
reduce_only: bool = False
|
||||||
|
post_only: bool = False
|
||||||
|
label: str | None = None
|
||||||
|
leverage: int | None = None # CER-016: None → default cap (3x)
|
||||||
|
|
||||||
|
model_config = {
|
||||||
|
"json_schema_extra": {
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"summary": "Market buy 0.1 BTC perpetual",
|
||||||
|
"value": {
|
||||||
|
"instrument_name": "BTC-PERPETUAL",
|
||||||
|
"type": "market",
|
||||||
|
"amount": 0.1,
|
||||||
|
"side": "buy",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ComboLeg(BaseModel):
|
||||||
|
instrument_name: str
|
||||||
|
direction: str # "buy" | "sell"
|
||||||
|
ratio: int = 1
|
||||||
|
|
||||||
|
|
||||||
|
class PlaceComboOrderReq(BaseModel):
|
||||||
|
legs: list[ComboLeg]
|
||||||
|
side: str # "buy" | "sell"
|
||||||
|
amount: float
|
||||||
|
type: str = "limit"
|
||||||
|
price: float | None = None
|
||||||
|
label: str | None = None
|
||||||
|
leverage: int | None = None
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def _at_least_two_legs(self):
|
||||||
|
if len(self.legs) < 2:
|
||||||
|
raise ValueError("combo requires at least 2 legs")
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class CancelOrderReq(BaseModel):
|
||||||
|
order_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class SetStopLossReq(BaseModel):
|
||||||
|
order_id: str
|
||||||
|
stop_price: float
|
||||||
|
|
||||||
|
|
||||||
|
class SetTakeProfitReq(BaseModel):
|
||||||
|
order_id: str
|
||||||
|
tp_price: float
|
||||||
|
|
||||||
|
|
||||||
|
class ClosePositionReq(BaseModel):
|
||||||
|
instrument_name: str
|
||||||
|
|
||||||
|
|
||||||
|
# === Tools (reads) ===
|
||||||
|
|
||||||
|
|
||||||
|
async def is_testnet(client: DeribitClient) -> dict:
|
||||||
|
return client.is_testnet()
|
||||||
|
|
||||||
|
|
||||||
|
async def environment_info(
|
||||||
|
client: DeribitClient, *, creds: dict, env_info: Any | None = None
|
||||||
|
) -> dict:
|
||||||
|
if env_info is None:
|
||||||
|
return {
|
||||||
|
"exchange": "deribit",
|
||||||
|
"environment": "testnet" if client.is_testnet().get("testnet") else "mainnet",
|
||||||
|
"source": "credentials",
|
||||||
|
"env_value": None,
|
||||||
|
"base_url": client.base_url,
|
||||||
|
"max_leverage": get_max_leverage(creds),
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"exchange": env_info.exchange,
|
||||||
|
"environment": env_info.environment,
|
||||||
|
"source": env_info.source,
|
||||||
|
"env_value": env_info.env_value,
|
||||||
|
"base_url": env_info.base_url,
|
||||||
|
"max_leverage": get_max_leverage(creds),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def get_ticker(client: DeribitClient, params: GetTickerReq) -> dict:
|
||||||
|
return await client.get_ticker(params.instrument_name)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_ticker_batch(client: DeribitClient, params: GetTickerBatchReq) -> dict:
|
||||||
|
return await client.get_ticker_batch(params.instrument_names)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_instruments(client: DeribitClient, params: GetInstrumentsReq) -> dict:
|
||||||
|
return await client.get_instruments(
|
||||||
|
currency=params.currency,
|
||||||
|
kind=params.kind,
|
||||||
|
expiry_from=params.expiry_from,
|
||||||
|
expiry_to=params.expiry_to,
|
||||||
|
strike_min=params.strike_min,
|
||||||
|
strike_max=params.strike_max,
|
||||||
|
min_open_interest=params.min_open_interest,
|
||||||
|
limit=params.limit,
|
||||||
|
offset=params.offset,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_orderbook(client: DeribitClient, params: GetOrderbookReq) -> dict:
|
||||||
|
return await client.get_orderbook(params.instrument_name, params.depth)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_orderbook_imbalance(
|
||||||
|
client: DeribitClient, params: OrderbookImbalanceReq
|
||||||
|
) -> dict:
|
||||||
|
return await client.get_orderbook_imbalance(params.instrument_name, params.depth)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_positions(client: DeribitClient, params: GetPositionsReq) -> dict:
|
||||||
|
return await client.get_positions(params.currency)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_account_summary(
|
||||||
|
client: DeribitClient, params: GetAccountSummaryReq
|
||||||
|
) -> dict:
|
||||||
|
return await client.get_account_summary(params.currency)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_trade_history(client: DeribitClient, params: GetTradeHistoryReq) -> dict:
|
||||||
|
return await client.get_trade_history(params.limit, params.instrument_name)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_historical(client: DeribitClient, params: GetHistoricalReq) -> dict:
|
||||||
|
return await client.get_historical(
|
||||||
|
params.instrument, params.start_date, params.end_date, params.resolution
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_dvol(client: DeribitClient, params: GetDvolReq) -> dict:
|
||||||
|
return await client.get_dvol(
|
||||||
|
params.currency, params.start_date, params.end_date, params.resolution
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_gex(client: DeribitClient, params: GetGexReq) -> dict:
|
||||||
|
return await client.get_gex(
|
||||||
|
params.currency, params.expiry_from, params.expiry_to, params.top_n_strikes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_dealer_gamma_profile(
|
||||||
|
client: DeribitClient, params: OptionFlowReq
|
||||||
|
) -> dict:
|
||||||
|
return await client.get_dealer_gamma_profile(
|
||||||
|
params.currency, params.expiry_from, params.expiry_to, params.top_n_strikes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_vanna_charm(client: DeribitClient, params: OptionFlowReq) -> dict:
|
||||||
|
return await client.get_vanna_charm(
|
||||||
|
params.currency, params.expiry_from, params.expiry_to, params.top_n_strikes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_oi_weighted_skew(client: DeribitClient, params: OptionFlowReq) -> dict:
|
||||||
|
return await client.get_oi_weighted_skew(
|
||||||
|
params.currency, params.expiry_from, params.expiry_to, params.top_n_strikes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_smile_asymmetry(client: DeribitClient, params: OptionFlowReq) -> dict:
|
||||||
|
return await client.get_smile_asymmetry(
|
||||||
|
params.currency, params.expiry_from, params.expiry_to, params.top_n_strikes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_atm_vs_wings_vol(client: DeribitClient, params: OptionFlowReq) -> dict:
|
||||||
|
return await client.get_atm_vs_wings_vol(
|
||||||
|
params.currency, params.expiry_from, params.expiry_to, params.top_n_strikes
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_pc_ratio(client: DeribitClient, params: GetPcRatioReq) -> dict:
|
||||||
|
return await client.get_pc_ratio(params.currency)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_skew_25d(client: DeribitClient, params: GetSkew25dReq) -> dict:
|
||||||
|
return await client.get_skew_25d(params.currency, params.expiry)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_term_structure(
|
||||||
|
client: DeribitClient, params: GetTermStructureReq
|
||||||
|
) -> dict:
|
||||||
|
return await client.get_term_structure(params.currency)
|
||||||
|
|
||||||
|
|
||||||
|
async def run_backtest(client: DeribitClient, params: RunBacktestReq) -> dict:
|
||||||
|
return await client.run_backtest(
|
||||||
|
strategy_name=params.strategy_name,
|
||||||
|
underlying=params.underlying,
|
||||||
|
lookback_days=params.lookback_days,
|
||||||
|
resolution=params.resolution,
|
||||||
|
entry_rules=params.entry_rules,
|
||||||
|
exit_rules=params.exit_rules,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def calculate_spread_payoff(
|
||||||
|
client: DeribitClient, params: CalculateSpreadPayoffReq
|
||||||
|
) -> dict:
|
||||||
|
return await client.calculate_spread_payoff(params.legs, params.quote_currency)
|
||||||
|
|
||||||
|
|
||||||
|
async def find_by_delta(client: DeribitClient, params: FindByDeltaReq) -> dict:
|
||||||
|
return await client.find_by_delta(
|
||||||
|
currency=params.currency,
|
||||||
|
expiry=params.expiry,
|
||||||
|
target_delta=params.target_delta,
|
||||||
|
option_type=params.option_type,
|
||||||
|
max_results=params.max_results,
|
||||||
|
min_open_interest=params.min_open_interest,
|
||||||
|
min_volume_24h=params.min_volume_24h,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_iv_rank(client: DeribitClient, params: GetIvRankReq) -> dict:
|
||||||
|
return await client.get_iv_rank(params.instrument)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_dvol_history(client: DeribitClient, params: GetDvolHistoryReq) -> dict:
|
||||||
|
return await client.get_dvol_history(params.currency, params.lookback_days)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_realized_vol(client: DeribitClient, params: GetRealizedVolReq) -> dict:
|
||||||
|
return await client.get_realized_vol(params.currency, params.windows)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_technical_indicators(
|
||||||
|
client: DeribitClient, params: GetIndicatorsReq
|
||||||
|
) -> dict:
|
||||||
|
return await client.get_technical_indicators(
|
||||||
|
params.instrument,
|
||||||
|
params.indicators,
|
||||||
|
params.start_date,
|
||||||
|
params.end_date,
|
||||||
|
params.resolution,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# === Tools (writes) ===
|
||||||
|
|
||||||
|
|
||||||
|
async def place_order(
|
||||||
|
client: DeribitClient, params: PlaceOrderReq, *, creds: dict
|
||||||
|
) -> dict:
|
||||||
|
cap_default = get_max_leverage(creds)
|
||||||
|
lev = _enforce_leverage(params.leverage, creds=creds, exchange="deribit")
|
||||||
|
if lev != cap_default:
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
await client.set_leverage(params.instrument_name, lev)
|
||||||
|
result = await client.place_order(
|
||||||
|
instrument_name=params.instrument_name,
|
||||||
|
side=params.side,
|
||||||
|
amount=params.amount,
|
||||||
|
type=params.type,
|
||||||
|
price=params.price,
|
||||||
|
reduce_only=params.reduce_only,
|
||||||
|
post_only=params.post_only,
|
||||||
|
label=params.label,
|
||||||
|
)
|
||||||
|
# TODO V2: wire audit via request.state.environment in router
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def place_combo_order(
|
||||||
|
client: DeribitClient, params: PlaceComboOrderReq, *, creds: dict
|
||||||
|
) -> dict:
|
||||||
|
cap_default = get_max_leverage(creds)
|
||||||
|
lev = _enforce_leverage(params.leverage, creds=creds, exchange="deribit")
|
||||||
|
if lev != cap_default:
|
||||||
|
for leg in params.legs:
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
await client.set_leverage(leg.instrument_name, lev)
|
||||||
|
result = await client.place_combo_order(
|
||||||
|
legs=[leg.model_dump() for leg in params.legs],
|
||||||
|
side=params.side,
|
||||||
|
amount=params.amount,
|
||||||
|
type=params.type,
|
||||||
|
price=params.price,
|
||||||
|
label=params.label,
|
||||||
|
)
|
||||||
|
# TODO V2: wire audit via request.state.environment in router
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def cancel_order(client: DeribitClient, params: CancelOrderReq) -> dict:
|
||||||
|
result = await client.cancel_order(params.order_id)
|
||||||
|
# TODO V2: wire audit via request.state.environment in router
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def set_stop_loss(client: DeribitClient, params: SetStopLossReq) -> dict:
|
||||||
|
result = await client.set_stop_loss(params.order_id, params.stop_price)
|
||||||
|
# TODO V2: wire audit via request.state.environment in router
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def set_take_profit(client: DeribitClient, params: SetTakeProfitReq) -> dict:
|
||||||
|
result = await client.set_take_profit(params.order_id, params.tp_price)
|
||||||
|
# TODO V2: wire audit via request.state.environment in router
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def close_position(client: DeribitClient, params: ClosePositionReq) -> dict:
|
||||||
|
result = await client.close_position(params.instrument_name)
|
||||||
|
# TODO V2: wire audit via request.state.environment in router
|
||||||
|
return result
|
||||||
Reference in New Issue
Block a user