Alpaca MCP Server
by tedlikeskix
import os
from typing import Dict, Any, List, Optional
from datetime import datetime, timedelta
from mcp.server.fastmcp import FastMCP
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import GetOrdersRequest, MarketOrderRequest, LimitOrderRequest
from alpaca.trading.enums import OrderSide, TimeInForce, QueryOrderStatus
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest, StockLatestQuoteRequest
from alpaca.data.timeframe import TimeFrame
# Initialize FastMCP server
mcp = FastMCP("alpaca-trading")
# Initialize Alpaca clients using environment variables
API_KEY = os.getenv("API_KEY_ID")
API_SECRET = os.getenv("API_SECRET_KEY")
# Check if keys are available
if not API_KEY or not API_SECRET:
raise ValueError("Alpaca API credentials not found in environment variables.")
# Initialize trading and data clients
trading_client = TradingClient(API_KEY, API_SECRET, paper=True)
stock_client = StockHistoricalDataClient(API_KEY, API_SECRET)
# Account information tools
@mcp.tool()
async def get_account_info() -> str:
"""Get the current account information including balances and status."""
account = trading_client.get_account()
info = f"""
Account Information:
-------------------
Account ID: {account.id}
Status: {account.status}
Currency: {account.currency}
Buying Power: ${float(account.buying_power):.2f}
Cash: ${float(account.cash):.2f}
Portfolio Value: ${float(account.portfolio_value):.2f}
Equity: ${float(account.equity):.2f}
Long Market Value: ${float(account.long_market_value):.2f}
Short Market Value: ${float(account.short_market_value):.2f}
Pattern Day Trader: {'Yes' if account.pattern_day_trader else 'No'}
Day Trades Remaining: {account.daytrade_count if hasattr(account, 'daytrade_count') else 'Unknown'}
"""
return info
@mcp.tool()
async def get_positions() -> str:
"""Get all current positions in the portfolio."""
positions = trading_client.get_all_positions()
if not positions:
return "No open positions found."
result = "Current Positions:\n-------------------\n"
for position in positions:
result += f"""
Symbol: {position.symbol}
Quantity: {position.qty} shares
Market Value: ${float(position.market_value):.2f}
Average Entry Price: ${float(position.avg_entry_price):.2f}
Current Price: ${float(position.current_price):.2f}
Unrealized P/L: ${float(position.unrealized_pl):.2f} ({float(position.unrealized_plpc) * 100:.2f}%)
-------------------
"""
return result
# Market data tools
@mcp.tool()
async def get_stock_quote(symbol: str) -> str:
"""
Get the latest quote for a stock.
Args:
symbol: Stock ticker symbol (e.g., AAPL, MSFT)
"""
try:
request_params = StockLatestQuoteRequest(symbol_or_symbols=symbol)
quotes = stock_client.get_stock_latest_quote(request_params)
if symbol in quotes:
quote = quotes[symbol]
return f"""
Latest Quote for {symbol}:
------------------------
Ask Price: ${quote.ask_price:.2f}
Bid Price: ${quote.bid_price:.2f}
Ask Size: {quote.ask_size}
Bid Size: {quote.bid_size}
Timestamp: {quote.timestamp}
"""
else:
return f"No quote data found for {symbol}."
except Exception as e:
return f"Error fetching quote for {symbol}: {str(e)}"
@mcp.tool()
async def get_stock_bars(symbol: str, days: int = 5) -> str:
"""
Get historical price bars for a stock.
Args:
symbol: Stock ticker symbol (e.g., AAPL, MSFT)
days: Number of trading days to look back (default: 5)
"""
try:
# Calculate start time based on days
start_time = datetime.now() - timedelta(days=days)
request_params = StockBarsRequest(
symbol_or_symbols=symbol,
timeframe=TimeFrame.Day,
start=start_time
)
bars = stock_client.get_stock_bars(request_params)
if symbol in bars and bars[symbol]:
result = f"Historical Data for {symbol} (Last {days} trading days):\n"
result += "---------------------------------------------------\n"
for bar in bars[symbol]:
result += f"Date: {bar.timestamp.date()}, Open: ${bar.open:.2f}, High: ${bar.high:.2f}, Low: ${bar.low:.2f}, Close: ${bar.close:.2f}, Volume: {bar.volume}\n"
return result
else:
return f"No historical data found for {symbol} in the last {days} days."
except Exception as e:
return f"Error fetching historical data for {symbol}: {str(e)}"
# Order management tools
@mcp.tool()
async def get_orders(status: str = "all", limit: int = 10) -> str:
"""
Get orders with the specified status.
Args:
status: Order status to filter by (open, closed, all)
limit: Maximum number of orders to return (default: 10)
"""
try:
# Convert status string to enum
if status.lower() == "open":
query_status = QueryOrderStatus.OPEN
elif status.lower() == "closed":
query_status = QueryOrderStatus.CLOSED
else:
query_status = QueryOrderStatus.ALL
request_params = GetOrdersRequest(
status=query_status,
limit=limit
)
orders = trading_client.get_orders(request_params)
if not orders:
return f"No {status} orders found."
result = f"{status.capitalize()} Orders (Last {len(orders)}):\n"
result += "-----------------------------------\n"
for order in orders:
result += f"""
Symbol: {order.symbol}
ID: {order.id}
Type: {order.type}
Side: {order.side}
Quantity: {order.qty}
Status: {order.status}
Submitted At: {order.submitted_at}
"""
if hasattr(order, 'filled_at') and order.filled_at:
result += f"Filled At: {order.filled_at}\n"
if hasattr(order, 'filled_avg_price') and order.filled_avg_price:
result += f"Filled Price: ${float(order.filled_avg_price):.2f}\n"
result += "-----------------------------------\n"
return result
except Exception as e:
return f"Error fetching orders: {str(e)}"
@mcp.tool()
async def place_market_order(symbol: str, side: str, quantity: float) -> str:
"""
Place a market order.
Args:
symbol: Stock ticker symbol (e.g., AAPL, MSFT)
side: Order side (buy or sell)
quantity: Number of shares to buy or sell
"""
try:
# Convert side string to enum
if side.lower() == "buy":
order_side = OrderSide.BUY
elif side.lower() == "sell":
order_side = OrderSide.SELL
else:
return f"Invalid order side: {side}. Must be 'buy' or 'sell'."
# Create market order request
order_data = MarketOrderRequest(
symbol=symbol,
qty=quantity,
side=order_side,
time_in_force=TimeInForce.DAY
)
# Submit order
order = trading_client.submit_order(order_data)
return f"""
Market Order Placed Successfully:
--------------------------------
Order ID: {order.id}
Symbol: {order.symbol}
Side: {order.side}
Quantity: {order.qty}
Type: {order.type}
Time In Force: {order.time_in_force}
Status: {order.status}
"""
except Exception as e:
return f"Error placing market order: {str(e)}"
@mcp.tool()
async def place_limit_order(symbol: str, side: str, quantity: float, limit_price: float) -> str:
"""
Place a limit order.
Args:
symbol: Stock ticker symbol (e.g., AAPL, MSFT)
side: Order side (buy or sell)
quantity: Number of shares to buy or sell
limit_price: Limit price for the order
"""
try:
# Convert side string to enum
if side.lower() == "buy":
order_side = OrderSide.BUY
elif side.lower() == "sell":
order_side = OrderSide.SELL
else:
return f"Invalid order side: {side}. Must be 'buy' or 'sell'."
# Create limit order request
order_data = LimitOrderRequest(
symbol=symbol,
qty=quantity,
side=order_side,
time_in_force=TimeInForce.DAY,
limit_price=limit_price
)
# Submit order
order = trading_client.submit_order(order_data)
return f"""
Limit Order Placed Successfully:
-------------------------------
Order ID: {order.id}
Symbol: {order.symbol}
Side: {order.side}
Quantity: {order.qty}
Type: {order.type}
Limit Price: ${float(order.limit_price):.2f}
Time In Force: {order.time_in_force}
Status: {order.status}
"""
except Exception as e:
return f"Error placing limit order: {str(e)}"
@mcp.tool()
async def cancel_all_orders() -> str:
"""Cancel all open orders."""
try:
cancel_statuses = trading_client.cancel_orders()
return f"Successfully canceled all open orders. Status: {cancel_statuses}"
except Exception as e:
return f"Error canceling orders: {str(e)}"
# Account management tools
@mcp.tool()
async def close_all_positions(cancel_orders: bool = True) -> str:
"""
Close all open positions.
Args:
cancel_orders: Whether to cancel all open orders before closing positions (default: True)
"""
try:
trading_client.close_all_positions(cancel_orders=cancel_orders)
return "Successfully closed all positions."
except Exception as e:
return f"Error closing positions: {str(e)}"
# Run the server
if __name__ == "__main__":
mcp.run(transport='stdio')