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