lint: ruff clean services/ (autofix + manual + ignore E741)
ci / ruff lint (push) Successful in 15s
ci / validate compose + Caddyfile (push) Successful in 2m6s
ci / mypy mcp_common (push) Successful in 30s
ci / pytest (push) Successful in 34s
ci / build & push to registry (push) Failing after 47s

- 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:
AdrianoDev
2026-04-29 08:44:12 +02:00
parent 910f80c99b
commit 9da2e12473
15 changed files with 29 additions and 41 deletions
+1 -1
View File
@@ -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 = [
+2 -3
View File
@@ -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
-1
View File
@@ -4,7 +4,6 @@ import asyncio
import httpx
import pytest
from mcp_common.http import async_client, call_with_retry
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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,
+6 -4
View File
@@ -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):