lint: ruff clean services/ (autofix + manual + ignore E741)
- 24 autofix safe (SIM105 contextlib.suppress, F401 unused imports, I001 import order, B007 unused loop var, F811 redef, F841 unused). - 15 unsafe-fix (UP038 X|Y in isinstance, SIM108 ternary, ecc.). - Manual fix: SIM102 nested if in deribit term_structure, E402 imports in test_cot.py + sentiment server.py. - Ignore E741 (variabili 'l' in list comprehensions deribit/client.py — stilistico, non bug). Tests: 478/478 verdi.
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@ target-version = "py313"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I", "W", "UP", "B", "SIM"]
|
||||
ignore = ["E501"]
|
||||
ignore = ["E501", "E741"]
|
||||
|
||||
[tool.ruff.lint.flake8-bugbear]
|
||||
extend-immutable-calls = [
|
||||
|
||||
@@ -21,6 +21,7 @@ Claude Code config esempio:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
@@ -63,10 +64,8 @@ def _derive_input_schemas(app: FastAPI, tool_names: list[str]) -> dict[str, dict
|
||||
if pname == "return":
|
||||
continue
|
||||
if isinstance(ann, type) and issubclass(ann, BaseModel):
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
out[name] = ann.model_json_schema()
|
||||
except Exception:
|
||||
pass
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
@@ -36,10 +36,7 @@ def orderbook_imbalance(
|
||||
ask_vol = sum(q for _, q in top_asks)
|
||||
total = bid_vol + ask_vol
|
||||
|
||||
if total == 0:
|
||||
ratio = None
|
||||
else:
|
||||
ratio = (bid_vol - ask_vol) / total
|
||||
ratio = None if total == 0 else (bid_vol - ask_vol) / total
|
||||
|
||||
# Microprice: best bid, best ask. Weighted by opposite-side size.
|
||||
microprice = None
|
||||
|
||||
@@ -4,7 +4,6 @@ import asyncio
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from mcp_common.http import async_client, call_with_retry
|
||||
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ def test_vol_cone_returns_percentiles_per_window():
|
||||
closes = _gbm_series(mu=0.0, sigma=0.5, n=400)
|
||||
out = vol_cone(closes, windows=[10, 30, 60])
|
||||
assert set(out.keys()) == {10, 30, 60}
|
||||
for w, stats in out.items():
|
||||
for _w, stats in out.items():
|
||||
assert "current" in stats
|
||||
assert "p10" in stats and "p50" in stats and "p90" in stats
|
||||
assert stats["p10"] <= stats["p50"] <= stats["p90"]
|
||||
@@ -200,7 +200,7 @@ def test_autocorrelation_white_noise_low():
|
||||
assert len(out) == 5
|
||||
# white noise → all autocorr ≈ 0 (within ±2/sqrt(N))
|
||||
bound = 2.0 / math.sqrt(len(rets))
|
||||
for lag, val in out.items():
|
||||
for _lag, val in out.items():
|
||||
assert abs(val) < bound * 2 # generous
|
||||
|
||||
|
||||
|
||||
@@ -71,13 +71,13 @@ def _asset_class_enum(ac: str) -> AssetClass:
|
||||
|
||||
def _serialize(obj: Any) -> Any:
|
||||
"""Recursively convert pydantic/datetime objects → json-safe."""
|
||||
if obj is None or isinstance(obj, (str, int, float, bool)):
|
||||
if obj is None or isinstance(obj, str | int | float | bool):
|
||||
return obj
|
||||
if isinstance(obj, (_dt.datetime, _dt.date)):
|
||||
if isinstance(obj, _dt.datetime | _dt.date):
|
||||
return obj.isoformat()
|
||||
if isinstance(obj, dict):
|
||||
return {k: _serialize(v) for k, v in obj.items()}
|
||||
if isinstance(obj, (list, tuple)):
|
||||
if isinstance(obj, list | tuple):
|
||||
return [_serialize(v) for v in obj]
|
||||
if hasattr(obj, "model_dump"):
|
||||
return _serialize(obj.model_dump())
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import time
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from mcp_common import indicators as ind
|
||||
from mcp_common import microstructure as micro
|
||||
from mcp_common import options as opt
|
||||
@@ -196,10 +196,8 @@ class DeribitClient:
|
||||
name = s.get("instrument_name")
|
||||
oi = s.get("open_interest")
|
||||
if name and oi is not None:
|
||||
try:
|
||||
with contextlib.suppress(TypeError, ValueError):
|
||||
oi_by_name[name] = float(oi)
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
all_items = raw.get("result") or []
|
||||
filtered: list[dict] = []
|
||||
@@ -882,8 +880,7 @@ class DeribitClient:
|
||||
shape = "backwardation"
|
||||
short_term = next((x for x in ts if 8 <= x["dte"] <= 14), None)
|
||||
mid_term = next((x for x in ts if 35 <= x["dte"] <= 45), None)
|
||||
if short_term and mid_term:
|
||||
if mid_term["atm_iv"] - short_term["atm_iv"] > 5:
|
||||
if short_term and mid_term and mid_term["atm_iv"] - short_term["atm_iv"] > 5:
|
||||
contango_steep = True
|
||||
calendar_opp = True
|
||||
|
||||
@@ -1131,7 +1128,7 @@ class DeribitClient:
|
||||
|
||||
structure = self._guess_structure(enriched)
|
||||
|
||||
notional = sum(l["quantity"] * spot for l in enriched) if spot else 0.0
|
||||
sum(l["quantity"] * spot for l in enriched) if spot else 0.0
|
||||
fee_per_leg = min(0.0003 * (spot or 1) * sum(l["quantity"] for l in enriched),
|
||||
0.125 * abs(net_premium)) if spot else 0.0
|
||||
fees_open = round(fee_per_leg, 4)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
@@ -272,10 +273,8 @@ def create_app(
|
||||
@asynccontextmanager
|
||||
async def _lifespan(_app: FastAPI):
|
||||
for inst in ("BTC-PERPETUAL", "ETH-PERPETUAL"):
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
await client.set_leverage(inst, cap_default)
|
||||
except Exception:
|
||||
pass
|
||||
yield
|
||||
|
||||
app = build_app(
|
||||
@@ -551,10 +550,8 @@ def create_app(
|
||||
_check(principal, core=True)
|
||||
lev = _enforce_leverage(body.leverage, creds=creds, exchange="deribit")
|
||||
if lev != cap_default:
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
await client.set_leverage(body.instrument_name, lev)
|
||||
except Exception:
|
||||
pass
|
||||
result = await client.place_order(
|
||||
instrument_name=body.instrument_name,
|
||||
side=body.side,
|
||||
@@ -582,10 +579,8 @@ def create_app(
|
||||
lev = _enforce_leverage(body.leverage, creds=creds, exchange="deribit")
|
||||
if lev != cap_default:
|
||||
for leg in body.legs:
|
||||
try:
|
||||
with contextlib.suppress(Exception):
|
||||
await client.set_leverage(leg.instrument_name, lev)
|
||||
except Exception:
|
||||
pass
|
||||
result = await client.place_combo_order(
|
||||
legs=[leg.model_dump() for leg in body.legs],
|
||||
side=body.side,
|
||||
|
||||
@@ -6,7 +6,6 @@ import asyncio
|
||||
import datetime as _dt
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from mcp_common import indicators as ind
|
||||
from mcp_common.http import async_client
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
import httpx
|
||||
from mcp_common.http import async_client
|
||||
|
||||
from mcp_macro.cot import classify_extreme, compute_percentile, parse_disagg_row, parse_tff_row
|
||||
from mcp_macro.cot_contracts import (
|
||||
ALL_DISAGG_SYMBOLS,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from mcp_macro.cot import classify_extreme, compute_percentile
|
||||
from mcp_macro.cot import (
|
||||
classify_extreme,
|
||||
compute_percentile,
|
||||
parse_disagg_row,
|
||||
parse_tff_row,
|
||||
)
|
||||
|
||||
|
||||
def test_compute_percentile_basic():
|
||||
@@ -44,9 +49,6 @@ def test_classify_extreme_none_input():
|
||||
assert classify_extreme(None) == "neutral"
|
||||
|
||||
|
||||
from mcp_macro.cot import parse_disagg_row, parse_tff_row
|
||||
|
||||
|
||||
# Payload Socrata reale (subset campi rilevanti, valori arbitrari per test)
|
||||
TFF_SOCRATA_ROW = {
|
||||
"report_date_as_yyyy_mm_dd": "2026-04-22T00:00:00.000",
|
||||
|
||||
@@ -366,6 +366,7 @@ async def test_fetch_cot_disagg_unknown_symbol():
|
||||
async def test_fetch_cot_extreme_positioning_flags_outliers(monkeypatch):
|
||||
"""Mock fetch_cot_tff e fetch_cot_disagg per simulare history e ultimo punto."""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from mcp_macro import fetchers as f
|
||||
|
||||
# Simula una serie ES dove ultimo lev_funds_net è in basso (extreme_short)
|
||||
|
||||
@@ -127,7 +127,6 @@ def test_get_market_overview_no_auth_401(http):
|
||||
assert r.status_code == 401
|
||||
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
|
||||
def test_get_cot_tff_core_ok(http):
|
||||
|
||||
@@ -5,7 +5,6 @@ import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from mcp_common.http import async_client
|
||||
|
||||
CRYPTOPANIC_URL = "https://cryptopanic.com/api/v1/posts/"
|
||||
|
||||
@@ -9,8 +9,6 @@ from mcp_common.mcp_bridge import mount_mcp_endpoint
|
||||
from mcp_common.server import build_app
|
||||
from pydantic import BaseModel
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from mcp_sentiment.fetchers import (
|
||||
fetch_cointegration_pairs,
|
||||
fetch_cross_exchange_funding,
|
||||
@@ -23,6 +21,8 @@ from mcp_sentiment.fetchers import (
|
||||
fetch_world_news,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# --- Body models ---
|
||||
|
||||
class GetCryptoNewsReq(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user