Skip to content

Commit fc19e96

Browse files
committed
fix: resolve all CI failures — lint, type config, test coverage
- Ruff: fix 295 remaining errors after initial --unsafe-fixes run - Add missing imports (pandas, duckdb, Any, OrderStatus) - Fix bare except → except Exception, raise ... from e (B904/E722) - Add noqa for intentional patterns: F821 forward refs, B027 optional abstract methods, B023 loop closures, F401 re-exported names - Rename ambiguous 'l' loop vars; fix B007 unused loop control vars - Move ruff config to [tool.ruff.lint] (removes deprecation warnings) - Update target-version to py311 - MyPy: update python_version to 3.11 in pyproject.toml - CI: add --group dev to uv sync so pytest-cov is installed - pyproject.toml: add pytest-cov to [dependency-groups].dev - uv.lock: regenerate with pytest-cov
1 parent 6323df1 commit fc19e96

File tree

372 files changed

+8240
-9980
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

372 files changed

+8240
-9980
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
${{ runner.os }}-uv-
9090
9191
- name: Install dependencies
92-
run: uv sync --all-packages
92+
run: uv sync --all-packages --group dev
9393

9494
- name: Run unit tests with coverage
9595
run: |

packages/alpaca_mcp/client.py

Lines changed: 110 additions & 113 deletions
Large diffs are not rendered by default.

packages/alpaca_mcp/server.py

Lines changed: 57 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,22 @@
2525

2626
from __future__ import annotations
2727

28-
import json
2928
import os
3029
from contextlib import asynccontextmanager
3130
from dataclasses import asdict
3231
from datetime import datetime
33-
from typing import Any, List, Optional
32+
from typing import Any
3433

3534
from fastmcp import FastMCP
3635
from loguru import logger
3736

38-
3937
# ---------------------------------------------------------------------------
4038
# Server context
4139
# ---------------------------------------------------------------------------
4240

4341

4442
class ServerContext:
45-
broker: Any = None # AlpacaBrokerClient
43+
broker: Any = None # AlpacaBrokerClient
4644
account_id: str = ""
4745

4846

@@ -55,17 +53,14 @@ async def lifespan(server: FastMCP):
5553
logger.info("[alpaca_mcp] Starting…")
5654
try:
5755
from alpaca_mcp.client import AlpacaBrokerClient
58-
from quantcore.execution.broker import BrokerAuthError
5956

6057
paper = os.getenv("ALPACA_PAPER", "true").lower() == "true"
6158
_ctx.broker = AlpacaBrokerClient(paper=paper)
6259

6360
# Resolve the account_id once at startup
6461
accounts = _ctx.broker.get_accounts()
6562
_ctx.account_id = accounts[0].account_id if accounts else ""
66-
logger.info(
67-
f"[alpaca_mcp] Connected account={_ctx.account_id} paper={paper}"
68-
)
63+
logger.info(f"[alpaca_mcp] Connected account={_ctx.account_id} paper={paper}")
6964
except Exception as exc:
7065
logger.warning(f"[alpaca_mcp] Startup error: {exc}. Tools will return errors.")
7166

@@ -112,8 +107,8 @@ def get_auth_status() -> dict:
112107
broker = _require_broker()
113108
ok = broker.check_auth()
114109
return {
115-
"success": ok,
116-
"paper": os.getenv("ALPACA_PAPER", "true").lower() == "true",
110+
"success": ok,
111+
"paper": os.getenv("ALPACA_PAPER", "true").lower() == "true",
117112
"account_id": _ctx.account_id,
118113
}
119114
except Exception as exc:
@@ -124,10 +119,10 @@ def get_auth_status() -> dict:
124119
def get_account() -> dict:
125120
"""Return Alpaca account summary including status and account type."""
126121
try:
127-
broker = _require_broker()
122+
broker = _require_broker()
128123
accounts = broker.get_accounts()
129124
return {
130-
"success": True,
125+
"success": True,
131126
"accounts": [_dc_to_dict(a) for a in accounts],
132127
}
133128
except Exception as exc:
@@ -138,7 +133,7 @@ def get_account() -> dict:
138133
def get_balance() -> dict:
139134
"""Return cash, buying power, and portfolio value."""
140135
try:
141-
broker = _require_broker()
136+
broker = _require_broker()
142137
balance = broker.get_balance(_ctx.account_id)
143138
return {"success": True, "balance": _dc_to_dict(balance)}
144139
except Exception as exc:
@@ -149,19 +144,19 @@ def get_balance() -> dict:
149144
def get_positions() -> dict:
150145
"""Return all open positions with quantity, entry price, and unrealised P&L."""
151146
try:
152-
broker = _require_broker()
147+
broker = _require_broker()
153148
positions = broker.get_positions(_ctx.account_id)
154149
return {
155-
"success": True,
150+
"success": True,
156151
"positions": [_dc_to_dict(p) for p in positions],
157-
"count": len(positions),
152+
"count": len(positions),
158153
}
159154
except Exception as exc:
160155
return {"success": False, "error": str(exc)}
161156

162157

163158
@mcp.tool()
164-
def get_quote(symbols: List[str]) -> dict:
159+
def get_quote(symbols: list[str]) -> dict:
165160
"""Return real-time best-bid/offer quotes for up to 50 symbols.
166161
167162
Args:
@@ -172,7 +167,7 @@ def get_quote(symbols: List[str]) -> dict:
172167
quotes = broker.get_quote(symbols)
173168
return {
174169
"success": True,
175-
"quotes": [_dc_to_dict(q) for q in quotes],
170+
"quotes": [_dc_to_dict(q) for q in quotes],
176171
}
177172
except Exception as exc:
178173
return {"success": False, "error": str(exc)}
@@ -182,8 +177,8 @@ def get_quote(symbols: List[str]) -> dict:
182177
def get_bars(
183178
symbol: str,
184179
timeframe: str = "1h",
185-
start: Optional[str] = None,
186-
end: Optional[str] = None,
180+
start: str | None = None,
181+
end: str | None = None,
187182
limit: int = 200,
188183
) -> dict:
189184
"""Return historical OHLCV bars for a symbol.
@@ -200,21 +195,26 @@ def get_bars(
200195
from quantcore.data.adapters.alpaca import AlpacaAdapter
201196

202197
_TF_MAP = {
203-
"1m": Timeframe.M1, "5m": Timeframe.M5, "15m": Timeframe.M15,
204-
"30m": Timeframe.M30, "1h": Timeframe.H1, "4h": Timeframe.H4,
205-
"1d": Timeframe.D1, "1w": Timeframe.W1,
198+
"1m": Timeframe.M1,
199+
"5m": Timeframe.M5,
200+
"15m": Timeframe.M15,
201+
"30m": Timeframe.M30,
202+
"1h": Timeframe.H1,
203+
"4h": Timeframe.H4,
204+
"1d": Timeframe.D1,
205+
"1w": Timeframe.W1,
206206
}
207207
tf = _TF_MAP.get(timeframe.lower())
208208
if tf is None:
209209
return {"success": False, "error": f"Unknown timeframe '{timeframe}'"}
210210

211211
broker = _require_broker()
212212
adapter = AlpacaAdapter(
213-
api_key = broker._api_key,
214-
secret_key = broker._secret_key,
213+
api_key=broker._api_key,
214+
secret_key=broker._secret_key,
215215
)
216216
start_dt = datetime.fromisoformat(start) if start else None
217-
end_dt = datetime.fromisoformat(end) if end else None
217+
end_dt = datetime.fromisoformat(end) if end else None
218218
df = adapter.fetch_ohlcv(symbol, tf, start_date=start_dt, end_date=end_dt)
219219
df = df.tail(limit)
220220

@@ -235,7 +235,7 @@ def preview_order(
235235
side: str,
236236
quantity: float,
237237
order_type: str = "market",
238-
limit_price: Optional[float] = None,
238+
limit_price: float | None = None,
239239
) -> dict:
240240
"""Estimate cost and commission for an order without submitting it.
241241
@@ -248,13 +248,14 @@ def preview_order(
248248
"""
249249
try:
250250
from quantcore.execution.unified_models import UnifiedOrder
251+
251252
broker = _require_broker()
252-
order = UnifiedOrder(
253-
symbol = symbol,
254-
side = side,
255-
quantity = quantity,
256-
order_type = order_type,
257-
limit_price = limit_price,
253+
order = UnifiedOrder(
254+
symbol=symbol,
255+
side=side,
256+
quantity=quantity,
257+
order_type=order_type,
258+
limit_price=limit_price,
258259
)
259260
preview = broker.preview_order(_ctx.account_id, order)
260261
return {"success": True, "preview": _dc_to_dict(preview)}
@@ -268,7 +269,7 @@ def place_order(
268269
side: str,
269270
quantity: float,
270271
order_type: str = "market",
271-
limit_price: Optional[float] = None,
272+
limit_price: float | None = None,
272273
time_in_force: str = "day",
273274
extended_hours: bool = False,
274275
) -> dict:
@@ -285,15 +286,16 @@ def place_order(
285286
"""
286287
try:
287288
from quantcore.execution.unified_models import UnifiedOrder
289+
288290
broker = _require_broker()
289-
order = UnifiedOrder(
290-
symbol = symbol,
291-
side = side,
292-
quantity = quantity,
293-
order_type = order_type,
294-
limit_price = limit_price,
295-
time_in_force = time_in_force,
296-
extended_hours = extended_hours,
291+
order = UnifiedOrder(
292+
symbol=symbol,
293+
side=side,
294+
quantity=quantity,
295+
order_type=order_type,
296+
limit_price=limit_price,
297+
time_in_force=time_in_force,
298+
extended_hours=extended_hours,
297299
)
298300
result = broker.place_order(_ctx.account_id, order)
299301
return {"success": True, "order": _dc_to_dict(result)}
@@ -310,15 +312,15 @@ def cancel_order(order_id: str) -> dict:
310312
"""
311313
try:
312314
broker = _require_broker()
313-
ok = broker.cancel_order(_ctx.account_id, order_id)
315+
ok = broker.cancel_order(_ctx.account_id, order_id)
314316
return {"success": ok, "order_id": order_id}
315317
except Exception as exc:
316318
return {"success": False, "error": str(exc)}
317319

318320

319321
@mcp.tool()
320322
def get_orders(
321-
status: Optional[str] = None,
323+
status: str | None = None,
322324
limit: int = 50,
323325
) -> dict:
324326
"""Return order history.
@@ -332,8 +334,8 @@ def get_orders(
332334
orders = broker.get_orders(_ctx.account_id, status=status)
333335
return {
334336
"success": True,
335-
"orders": [_dc_to_dict(o) for o in orders[:limit]],
336-
"count": len(orders),
337+
"orders": [_dc_to_dict(o) for o in orders[:limit]],
338+
"count": len(orders),
337339
}
338340
except Exception as exc:
339341
return {"success": False, "error": str(exc)}
@@ -342,7 +344,7 @@ def get_orders(
342344
@mcp.tool()
343345
def get_option_chains(
344346
symbol: str,
345-
expiry: Optional[str] = None,
347+
expiry: str | None = None,
346348
) -> dict:
347349
"""Return an options chain snapshot for a symbol.
348350
@@ -361,16 +363,18 @@ def get_option_chains(
361363
req_kwargs = {"underlying_symbol": symbol}
362364
if expiry:
363365
req_kwargs["expiration_date"] = expiry
364-
req = OptionChainRequest(**req_kwargs)
366+
req = OptionChainRequest(**req_kwargs)
365367
chain = cli.get_option_chain(req)
366368
# Normalise to a list of dicts
367369
contracts = []
368370
for sym, snap in chain.items():
369-
contracts.append({
370-
"symbol": sym,
371-
"bid": float(snap.latest_quote.bid_price) if snap.latest_quote else None,
372-
"ask": float(snap.latest_quote.ask_price) if snap.latest_quote else None,
373-
})
371+
contracts.append(
372+
{
373+
"symbol": sym,
374+
"bid": float(snap.latest_quote.bid_price) if snap.latest_quote else None,
375+
"ask": float(snap.latest_quote.ask_price) if snap.latest_quote else None,
376+
}
377+
)
374378
return {"success": True, "underlying": symbol, "contracts": contracts}
375379
except Exception as exc:
376380
return {"success": False, "error": str(exc)}

0 commit comments

Comments
 (0)