TradingAgents/scripts/update_positions.py

204 lines
6.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Position Updater Script
This script:
1. Fetches current prices for all open positions
2. Updates positions with latest price data
3. Calculates return % for each position
4. Can be run manually or via cron for continuous monitoring
Usage:
python scripts/update_positions.py
"""
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from datetime import datetime
import yfinance as yf
from tradingagents.dataflows.discovery.performance.position_tracker import PositionTracker
from tradingagents.utils.logger import get_logger
logger = get_logger(__name__)
def fetch_current_prices(tickers):
"""
Fetch current prices for given tickers using yfinance.
Handles both single and multiple tickers with appropriate error handling.
Args:
tickers: List of ticker symbols
Returns:
Dictionary mapping ticker to current price (or None if fetch failed)
"""
prices = {}
if not tickers:
return prices
# Try to download all tickers at once for efficiency
try:
if len(tickers) == 1:
# Single ticker - yfinance returns Series instead of DataFrame
ticker = tickers[0]
data = yf.download(
ticker,
period="1d",
progress=False,
auto_adjust=True,
)
if not data.empty:
# For single ticker with period='1d', get the latest close
prices[ticker] = float(data["Close"].iloc[-1])
else:
logger.warning(f"Could not fetch data for {ticker}")
prices[ticker] = None
else:
# Multiple tickers - yfinance returns DataFrame with MultiIndex
data = yf.download(
tickers,
period="1d",
progress=False,
auto_adjust=True,
)
if not data.empty:
# Get the latest close for each ticker
if len(tickers) > 1:
for ticker in tickers:
if ticker in data.columns:
close_price = data[ticker]["Close"]
if not close_price.empty:
prices[ticker] = float(close_price.iloc[-1])
else:
prices[ticker] = None
else:
prices[ticker] = None
else:
# Edge case: single ticker in batch download
if "Close" in data.columns:
prices[tickers[0]] = float(data["Close"].iloc[-1])
else:
prices[tickers[0]] = None
else:
for ticker in tickers:
prices[ticker] = None
except Exception as e:
logger.warning(f"Batch download failed: {e}")
# Fall back to per-ticker download
for ticker in tickers:
try:
data = yf.download(
ticker,
period="1d",
progress=False,
auto_adjust=True,
)
if not data.empty:
prices[ticker] = float(data["Close"].iloc[-1])
else:
prices[ticker] = None
except Exception as e:
logger.error(f"Failed to fetch price for {ticker}: {e}")
prices[ticker] = None
return prices
def main():
"""
Main function to update all open positions with current prices.
Process:
1. Initialize PositionTracker
2. Load all open positions
3. Get unique tickers
4. Fetch current prices via yfinance
5. Update each position with new price
6. Save updated positions
7. Print progress messages
"""
logger.info("""
╔══════════════════════════════════════════════════════════════╗
║ TradingAgents - Position Updater ║
╚══════════════════════════════════════════════════════════════╝""".strip())
# Initialize position tracker
tracker = PositionTracker(data_dir="data")
# Load all open positions
logger.info("📂 Loading open positions...")
positions = tracker.load_all_open_positions()
if not positions:
logger.info("✅ No open positions to update.")
return
logger.info(f"✅ Found {len(positions)} open position(s)")
# Get unique tickers
tickers = list({pos["ticker"] for pos in positions})
logger.info(f"📊 Fetching current prices for {len(tickers)} unique ticker(s)...")
logger.info(f"Tickers: {', '.join(sorted(tickers))}")
# Fetch current prices
prices = fetch_current_prices(tickers)
# Update positions and track results
updated_count = 0
failed_count = 0
for position in positions:
ticker = position["ticker"]
current_price = prices.get(ticker)
if current_price is None:
logger.error(f"{ticker}: Failed to fetch price - position not updated")
failed_count += 1
continue
# Update position with new price
entry_price = position["entry_price"]
return_pct = ((current_price - entry_price) / entry_price) * 100
# Update the position
position = tracker.update_position_price(position, current_price)
# Save the updated position
tracker.save_position(position)
# Log progress
return_symbol = "📈" if return_pct >= 0 else "📉"
logger.info(
f"{return_symbol} {ticker:6} | Price: ${current_price:8.2f} | Return: {return_pct:+7.2f}%"
)
updated_count += 1
# Summary
logger.info("=" * 60)
logger.info("✅ Update Summary:")
logger.info(f"Updated: {updated_count}/{len(positions)} positions")
logger.info(f"Failed: {failed_count}/{len(positions)} positions")
logger.info(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}")
logger.info("=" * 60)
if updated_count > 0:
logger.info("🎉 Position update complete!")
else:
logger.warning("No positions were successfully updated.")
if __name__ == "__main__":
main()