Spaces:
Sleeping
Sleeping
| import yfinance as yf | |
| from typing import Dict, Any, Optional, Literal | |
| from config import INITIAL_CAPITAL | |
| from tools.alpaca_broker import get_broker | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| # Get the Alpaca broker instance | |
| try: | |
| broker = get_broker() | |
| logger.info("Execution module using Alpaca broker") | |
| except Exception as e: | |
| logger.error(f"Failed to initialize Alpaca broker: {e}") | |
| broker = None | |
| def get_positions() -> Dict[str, Any]: | |
| """Get current positions and cash. Example: get_positions()""" | |
| if broker is None: | |
| return { | |
| "error": "Alpaca broker not initialized. Check your API credentials.", | |
| "cash": 0, | |
| "positions": {} | |
| } | |
| try: | |
| account = broker.get_account() | |
| positions_raw = broker.get_all_positions() | |
| # Convert to simple format: {symbol: qty} | |
| positions = { | |
| symbol: details['qty'] | |
| for symbol, details in positions_raw.items() | |
| } | |
| return { | |
| "cash": account['cash'], | |
| "equity": account['equity'], | |
| "buying_power": account['buying_power'], | |
| "positions": positions | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to get positions: {e}") | |
| return { | |
| "error": str(e), | |
| "cash": 0, | |
| "positions": {} | |
| } | |
| def place_order( | |
| symbol: str, | |
| side: Literal["buy", "sell"], | |
| qty: float, | |
| order_type: Literal["market", "limit"] = "market", | |
| limit_price: Optional[float] = None | |
| ) -> str: | |
| """Place a trade order. Example: place_order("AAPL", "buy", 10) or place_order("TSLA", "sell", 5, order_type="limit", limit_price=250.00)""" | |
| if broker is None: | |
| return "ERROR: Alpaca broker not initialized. Check your API credentials in .env file." | |
| # Import here to avoid circular dependency | |
| from tools.risk_engine import validate_trade | |
| try: | |
| # Get current price for risk validation | |
| ticker = yf.Ticker(symbol) | |
| try: | |
| current_price = ticker.fast_info.last_price | |
| except: | |
| return f"Failed to get price for {symbol}" | |
| if current_price is None: | |
| return f"Failed to get price for {symbol}" | |
| # Pre-Trade Risk Check | |
| risk_error = validate_trade(symbol, side, qty, current_price) | |
| if risk_error: | |
| logger.warning(f"Trade rejected by risk engine: {risk_error}") | |
| return risk_error | |
| # Submit order to Alpaca | |
| if order_type == "market": | |
| logger.info(f"Submitting MARKET order: {side.upper()} {qty} {symbol}") | |
| order_result = broker.submit_market_order(symbol, side, qty) | |
| logger.info(f"Order submitted successfully: ID={order_result['order_id']}") | |
| return ( | |
| f"✅ Market Order Submitted: {side.upper()} {qty} {symbol}\n" | |
| f"Order ID: {order_result['order_id']}\n" | |
| f"Status: {order_result['status']}\n" | |
| f"Submitted at: {order_result['submitted_at']}" | |
| ) | |
| elif order_type == "limit": | |
| if not limit_price: | |
| return "ERROR: Limit price required for limit orders." | |
| # Validate limit price direction | |
| if side == "buy" and limit_price > current_price: | |
| logger.warning(f"Buy limit {limit_price} is above market {current_price}") | |
| if side == "sell" and limit_price < current_price: | |
| logger.warning(f"Sell limit {limit_price} is below market {current_price}") | |
| logger.info(f"Submitting LIMIT order: {side.upper()} {qty} {symbol} @ ${limit_price}") | |
| order_result = broker.submit_limit_order(symbol, side, qty, limit_price) | |
| logger.info(f"Order submitted successfully: ID={order_result['order_id']}") | |
| return ( | |
| f"✅ Limit Order Submitted: {side.upper()} {qty} {symbol} @ ${limit_price:.2f}\n" | |
| f"Order ID: {order_result['order_id']}\n" | |
| f"Status: {order_result['status']}\n" | |
| f"Submitted at: {order_result['submitted_at']}" | |
| ) | |
| else: | |
| logger.error(f"Unknown order type requested: {order_type}") | |
| return f"ERROR: Unknown order type: {order_type}" | |
| except Exception as e: | |
| logger.error(f"Order failed: {e}", exc_info=True) | |
| return f"ERROR: Order failed - {str(e)}" | |
| def cancel_order(order_id: str) -> str: | |
| """Cancel an order. Example: cancel_order("6c7d...89a")""" | |
| if broker is None: | |
| return "ERROR: Alpaca broker not initialized." | |
| try: | |
| logger.info(f"Cancelling order: {order_id}") | |
| broker.cancel_order(order_id) | |
| logger.info(f"Order {order_id} cancelled successfully") | |
| return f"✅ Order {order_id} cancelled successfully" | |
| except Exception as e: | |
| logger.error(f"Cancel order failed: {e}", exc_info=True) | |
| return f"ERROR: Failed to cancel order - {str(e)}" | |
| def flatten() -> str: | |
| """Close all positions. Example: flatten()""" | |
| if broker is None: | |
| return "ERROR: Alpaca broker not initialized." | |
| try: | |
| result = broker.close_all_positions() | |
| if result['closed_count'] == 0: | |
| return "No positions to flatten." | |
| msg = [f"✅ Flattened {result['closed_count']} positions:"] | |
| for pos in result['positions_closed']: | |
| msg.append(f" - {pos['symbol']}: {pos['qty']} shares ({pos['status']})") | |
| return "\n".join(msg) | |
| except Exception as e: | |
| logger.error(f"Flatten failed: {e}") | |
| return f"ERROR: Failed to flatten positions - {str(e)}" | |
| def get_order_history(status: str = "all") -> str: | |
| """Get order history. Example: get_order_history(status="closed")""" | |
| if broker is None: | |
| return "ERROR: Alpaca broker not initialized." | |
| try: | |
| orders = broker.get_orders(status) | |
| if not orders: | |
| return f"No {status} orders found." | |
| msg = [f"=== {status.upper()} ORDERS ({len(orders)}) ==="] | |
| for order in orders[:10]: # Limit to 10 most recent | |
| msg.append( | |
| f"{order['symbol']}: {order['side'].upper()} {order['qty']} " | |
| f"({order['type']}, {order['status']}) - {order['submitted_at']}" | |
| ) | |
| if len(orders) > 10: | |
| msg.append(f"\n... and {len(orders) - 10} more orders") | |
| return "\n".join(msg) | |
| except Exception as e: | |
| logger.error(f"Get order history failed: {e}") | |
| return f"ERROR: Failed to get order history - {str(e)}" | |