TradingAgents/cli/indian_cli.py

485 lines
18 KiB
Python

"""
Indian Market CLI Interface
Command-line interface for Indian stock market analysis and trading
"""
import click
import json
import sys
from datetime import datetime, timedelta
from typing import Dict, Any, List
import logging
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
try:
from tradingagents.agents.utils.indian_agent_toolkit import indian_toolkit
from tradingagents.indian_config import get_major_stocks, get_sector_stocks, INDIAN_SECTORS
from tradingagents.dataflows.ticker_utils import format_indian_ticker, validate_indian_ticker
except ImportError as e:
logger.error(f"Failed to import required modules: {e}")
sys.exit(1)
@click.group()
@click.version_option(version='1.0.0')
def indian_cli():
"""Indian Stock Market Analysis CLI
A comprehensive command-line interface for analyzing Indian stocks,
market conditions, and generating trading insights.
"""
pass
@indian_cli.group()
def market():
"""Market analysis commands"""
pass
@indian_cli.group()
def stock():
"""Individual stock analysis commands"""
pass
@indian_cli.group()
def portfolio():
"""Portfolio management commands"""
pass
@indian_cli.group()
def sector():
"""Sector analysis commands"""
pass
@indian_cli.group()
def utils():
"""Utility commands"""
pass
# Market Commands
@market.command()
@click.option('--date', default=None, help='Analysis date (YYYY-MM-DD), defaults to today')
@click.option('--output', type=click.Choice(['json', 'text']), default='text', help='Output format')
def overview(date, output):
"""Get Indian market overview"""
try:
if date is None:
date = datetime.now().strftime("%Y-%m-%d")
click.echo(f"📊 Getting Indian market overview for {date}...")
# Get market overview
market_overview = indian_toolkit.get_market_overview(date)
# Get market conditions analysis
market_conditions = indian_toolkit.analyze_market_conditions()
if output == 'json':
result = {
"date": date,
"market_overview": market_overview,
"market_conditions": market_conditions
}
click.echo(json.dumps(result, indent=2))
else:
click.echo("\n" + "="*80)
click.echo(f"🇮🇳 INDIAN MARKET OVERVIEW - {date}")
click.echo("="*80)
click.echo(market_overview)
click.echo("\n" + "="*80)
click.echo("📈 MARKET CONDITIONS ANALYSIS")
click.echo("="*80)
click.echo(market_conditions.get('market_conditions', 'Analysis not available'))
# Display key metrics
if 'trading_bias' in market_conditions:
click.echo(f"\n🎯 Trading Bias: {market_conditions['trading_bias']}")
if 'confidence' in market_conditions:
click.echo(f"📊 Confidence Level: {market_conditions['confidence']:.1%}")
except Exception as e:
click.echo(f"❌ Error getting market overview: {e}", err=True)
sys.exit(1)
@market.command()
@click.option('--output', type=click.Choice(['json', 'text']), default='text', help='Output format')
def status():
"""Check current market status"""
try:
status_info = indian_toolkit.check_market_status()
if output == 'json':
click.echo(json.dumps(status_info, indent=2))
else:
click.echo("\n" + "="*50)
click.echo("🇮🇳 INDIAN MARKET STATUS")
click.echo("="*50)
market_status = status_info['market_status']
is_open = status_info['is_market_open']
status_emoji = "🟢" if is_open else "🔴"
click.echo(f"{status_emoji} Market Status: {market_status.upper()}")
click.echo(f"⏰ Current Time: {status_info['current_time']}")
trading_hours = status_info['trading_hours']
click.echo(f"🕘 Trading Hours: {trading_hours['open']} - {trading_hours['close']} IST")
if not is_open:
if market_status == 'pre_market':
click.echo("💡 Market opens in a few minutes. Prepare your watchlist!")
elif market_status == 'closed':
click.echo("💡 Market is closed. Good time for analysis and planning!")
elif market_status == 'closed_weekend':
click.echo("💡 Weekend - Markets closed. Time for research!")
except Exception as e:
click.echo(f"❌ Error checking market status: {e}", err=True)
sys.exit(1)
# Stock Commands
@stock.command()
@click.argument('symbol')
@click.option('--exchange', default='NSE', type=click.Choice(['NSE', 'BSE']), help='Exchange')
@click.option('--days', default=30, help='Number of days of data')
@click.option('--output', type=click.Choice(['json', 'text']), default='text', help='Output format')
def analyze(symbol, exchange, days, output):
"""Comprehensive stock analysis"""
try:
symbol = symbol.upper()
click.echo(f"🔍 Analyzing {symbol} on {exchange}...")
# Get comprehensive analysis
analysis = indian_toolkit.analyze_fundamentals(symbol, exchange)
technical = indian_toolkit.analyze_technical(symbol, exchange, days)
risk_assessment = indian_toolkit.assess_stock_risk(symbol, exchange)
if output == 'json':
result = {
"symbol": symbol,
"exchange": exchange,
"fundamental_analysis": analysis,
"technical_analysis": technical,
"risk_assessment": risk_assessment
}
click.echo(json.dumps(result, indent=2))
else:
click.echo("\n" + "="*80)
click.echo(f"📊 COMPREHENSIVE ANALYSIS: {symbol}.{exchange}")
click.echo("="*80)
# Fundamental Analysis
click.echo("\n🏢 FUNDAMENTAL ANALYSIS")
click.echo("-" * 50)
if 'analysis' in analysis:
click.echo(analysis['analysis'])
if 'recommendation' in analysis:
click.echo(f"\n💡 Investment Recommendation:")
click.echo(analysis['recommendation'])
# Technical Analysis
click.echo(f"\n📈 TECHNICAL ANALYSIS ({days} days)")
click.echo("-" * 50)
if 'technical_analysis' in technical:
click.echo(technical['technical_analysis'])
# Risk Assessment
click.echo("\n⚠️ RISK ASSESSMENT")
click.echo("-" * 50)
if 'overall_risk_score' in risk_assessment:
risk_score = risk_assessment['overall_risk_score']
click.echo(f"Risk Score: {risk_score:.1f}/10")
click.echo(f"Risk Level: {risk_assessment.get('recommendation', 'N/A')}")
# Key Metrics Summary
click.echo("\n📋 SUMMARY")
click.echo("-" * 50)
click.echo(f"Symbol: {symbol}.{exchange}")
click.echo(f"Analysis Date: {analysis.get('analysis_date', 'N/A')}")
click.echo(f"Fundamental Confidence: {analysis.get('confidence', 0):.1%}")
click.echo(f"Technical Confidence: {technical.get('confidence', 0):.1%}")
except Exception as e:
click.echo(f"❌ Error analyzing {symbol}: {e}", err=True)
sys.exit(1)
@stock.command()
@click.argument('symbol')
@click.option('--exchange', default='NSE', type=click.Choice(['NSE', 'BSE']), help='Exchange')
@click.option('--output', type=click.Choice(['json', 'text']), default='text', help='Output format')
def quote(symbol, exchange, output):
"""Get real-time stock quote"""
try:
symbol = symbol.upper()
click.echo(f"💹 Getting quote for {symbol} on {exchange}...")
quote_data = indian_toolkit.get_indian_stock_quote(symbol, exchange)
if output == 'json':
result = {
"symbol": symbol,
"exchange": exchange,
"quote": quote_data
}
click.echo(json.dumps(result, indent=2))
else:
click.echo("\n" + "="*50)
click.echo(f"💹 REAL-TIME QUOTE: {symbol}.{exchange}")
click.echo("="*50)
click.echo(quote_data)
except Exception as e:
click.echo(f"❌ Error getting quote for {symbol}: {e}", err=True)
sys.exit(1)
@stock.command()
@click.argument('symbol')
@click.option('--days', default=30, help='Number of days of historical data')
@click.option('--exchange', default='NSE', type=click.Choice(['NSE', 'BSE']), help='Exchange')
@click.option('--output', type=click.Choice(['json', 'text', 'csv']), default='text', help='Output format')
def data(symbol, days, exchange, output):
"""Get historical stock data"""
try:
symbol = symbol.upper()
end_date = datetime.now().strftime("%Y-%m-%d")
start_date = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
click.echo(f"📊 Getting {days} days of data for {symbol} on {exchange}...")
stock_data = indian_toolkit.get_indian_stock_data(symbol, start_date, end_date, exchange)
if output == 'json':
result = {
"symbol": symbol,
"exchange": exchange,
"start_date": start_date,
"end_date": end_date,
"data": stock_data
}
click.echo(json.dumps(result, indent=2))
elif output == 'csv':
click.echo(stock_data)
else:
click.echo("\n" + "="*60)
click.echo(f"📊 HISTORICAL DATA: {symbol}.{exchange}")
click.echo(f"Period: {start_date} to {end_date}")
click.echo("="*60)
click.echo(stock_data)
except Exception as e:
click.echo(f"❌ Error getting data for {symbol}: {e}", err=True)
sys.exit(1)
# Sector Commands
@sector.command()
@click.argument('sector_name')
@click.option('--date', default=None, help='Analysis date (YYYY-MM-DD)')
@click.option('--output', type=click.Choice(['json', 'text']), default='text', help='Output format')
def analyze(sector_name, date, output):
"""Analyze specific sector"""
try:
sector_name = sector_name.lower()
if date is None:
date = datetime.now().strftime("%Y-%m-%d")
click.echo(f"🏭 Analyzing {sector_name.title()} sector...")
sector_analysis = indian_toolkit.get_sector_analysis(sector_name, date)
sector_stocks = indian_toolkit.get_sector_stocks(sector_name)
if output == 'json':
result = {
"sector": sector_name,
"analysis_date": date,
"analysis": sector_analysis,
"stocks": sector_stocks
}
click.echo(json.dumps(result, indent=2))
else:
click.echo("\n" + "="*60)
click.echo(f"🏭 SECTOR ANALYSIS: {sector_name.upper()}")
click.echo("="*60)
click.echo(sector_analysis)
if sector_stocks:
click.echo(f"\n📈 KEY STOCKS IN {sector_name.upper()} SECTOR:")
click.echo("-" * 40)
for i, stock in enumerate(sector_stocks[:10], 1):
click.echo(f"{i:2d}. {stock}")
if len(sector_stocks) > 10:
click.echo(f" ... and {len(sector_stocks) - 10} more")
except Exception as e:
click.echo(f"❌ Error analyzing sector {sector_name}: {e}", err=True)
sys.exit(1)
@sector.command()
def list():
"""List available sectors"""
try:
click.echo("\n🏭 AVAILABLE SECTORS FOR ANALYSIS")
click.echo("="*50)
for i, sector in enumerate(INDIAN_SECTORS.keys(), 1):
stock_count = len(INDIAN_SECTORS[sector])
click.echo(f"{i:2d}. {sector.title():<15} ({stock_count} stocks)")
click.echo(f"\n💡 Use 'sector analyze <sector_name>' to analyze a specific sector")
except Exception as e:
click.echo(f"❌ Error listing sectors: {e}", err=True)
sys.exit(1)
# Portfolio Commands
@portfolio.command()
@click.option('--file', type=click.Path(exists=True), help='Portfolio JSON file')
@click.option('--output', type=click.Choice(['json', 'text']), default='text', help='Output format')
def analyze(file, output):
"""Analyze portfolio performance"""
try:
if not file:
click.echo("❌ Please provide a portfolio file with --file option", err=True)
sys.exit(1)
click.echo(f"📊 Analyzing portfolio from {file}...")
# Load portfolio data
with open(file, 'r') as f:
portfolio_data = json.load(f)
holdings = portfolio_data.get('holdings', [])
# Calculate portfolio metrics
metrics = indian_toolkit.calculate_portfolio_metrics(holdings)
if output == 'json':
click.echo(json.dumps(metrics, indent=2))
else:
click.echo("\n" + "="*60)
click.echo("📊 PORTFOLIO ANALYSIS")
click.echo("="*60)
click.echo(f"Total Investment: ₹{metrics['total_investment']:,.2f}")
click.echo(f"Current Value: ₹{metrics['total_current_value']:,.2f}")
click.echo(f"Total P&L: ₹{metrics['total_pnl']:,.2f}")
click.echo(f"Total P&L %: {metrics['total_pnl_percentage']:.2f}%")
click.echo(f"\n📈 INDIVIDUAL HOLDINGS:")
click.echo("-" * 60)
for holding in metrics['holdings']:
pnl_emoji = "🟢" if holding['pnl'] >= 0 else "🔴"
click.echo(f"{pnl_emoji} {holding['symbol']:<10} | "
f"Qty: {holding['quantity']:>6} | "
f"P&L: ₹{holding['pnl']:>8.2f} ({holding['pnl_percentage']:>6.2f}%)")
except Exception as e:
click.echo(f"❌ Error analyzing portfolio: {e}", err=True)
sys.exit(1)
@portfolio.command()
@click.argument('symbol')
@click.argument('entry_price', type=float)
@click.argument('stop_loss', type=float)
@click.argument('portfolio_value', type=float)
@click.option('--risk', default=2.0, help='Risk percentage (default: 2%)')
@click.option('--exchange', default='NSE', type=click.Choice(['NSE', 'BSE']), help='Exchange')
def position_size(symbol, entry_price, stop_loss, portfolio_value, risk, exchange):
"""Calculate optimal position size"""
try:
symbol = symbol.upper()
risk_decimal = risk / 100
click.echo(f"📊 Calculating position size for {symbol}...")
position_calc = indian_toolkit.calculate_position_size(
symbol, entry_price, stop_loss, portfolio_value, risk_decimal
)
if 'error' in position_calc:
click.echo(f"❌ Error: {position_calc['error']}", err=True)
sys.exit(1)
click.echo("\n" + "="*60)
click.echo(f"📊 POSITION SIZE CALCULATION: {symbol}.{exchange}")
click.echo("="*60)
click.echo(f"Entry Price: ₹{entry_price:,.2f}")
click.echo(f"Stop Loss: ₹{stop_loss:,.2f}")
click.echo(f"Portfolio Value: ₹{portfolio_value:,.2f}")
click.echo(f"Risk Tolerance: {risk}%")
click.echo(f"\n📈 RECOMMENDATION:")
click.echo(f"Recommended Shares: {position_calc['recommended_shares']:,}")
click.echo(f"Position Value: ₹{position_calc['position_value']:,.2f}")
click.echo(f"Risk Amount: ₹{position_calc['risk_amount']:,.2f}")
click.echo(f"Position %: {position_calc['position_percentage']:.2f}%")
click.echo(f"Risk %: {position_calc['risk_percentage']:.2f}%")
click.echo(f"Risk-Reward: {position_calc['risk_reward_ratio']}")
except Exception as e:
click.echo(f"❌ Error calculating position size: {e}", err=True)
sys.exit(1)
# Utility Commands
@utils.command()
@click.argument('symbol')
@click.option('--exchange', default='NSE', type=click.Choice(['NSE', 'BSE']), help='Exchange')
def validate(symbol, exchange):
"""Validate ticker symbol"""
try:
formatted_ticker = format_indian_ticker(symbol, exchange)
is_valid = validate_indian_ticker(formatted_ticker)
click.echo(f"\n🔍 TICKER VALIDATION")
click.echo("="*30)
click.echo(f"Input: {symbol}")
click.echo(f"Formatted: {formatted_ticker}")
click.echo(f"Valid: {'✅ Yes' if is_valid else '❌ No'}")
except Exception as e:
click.echo(f"❌ Error validating ticker: {e}", err=True)
sys.exit(1)
@utils.command()
def stocks():
"""List major Indian stocks"""
try:
major_stocks = get_major_stocks()
click.echo("\n🏢 MAJOR INDIAN STOCKS")
click.echo("="*60)
for symbol, info in major_stocks.items():
click.echo(f"{symbol:<12} | {info['name']:<30} | {info['sector']:<15}")
click.echo(f"\n📊 Total: {len(major_stocks)} stocks")
except Exception as e:
click.echo(f"❌ Error listing stocks: {e}", err=True)
sys.exit(1)
@utils.command()
def config():
"""Show Indian market configuration"""
try:
from tradingagents.indian_config import get_indian_config
config = get_indian_config()
click.echo("\n⚙️ INDIAN MARKET CONFIGURATION")
click.echo("="*50)
click.echo(f"Market Region: {config['market_region']}")
click.echo(f"Currency: {config['currency']}")
click.echo(f"Timezone: {config['timezone']}")
click.echo(f"Primary Exchange: {config['exchanges']['primary']}")
click.echo(f"Trading Hours: {config['trading_hours']['open']} - {config['trading_hours']['close']}")
click.echo(f"Settlement: {config['market_parameters']['settlement']}")
except Exception as e:
click.echo(f"❌ Error showing config: {e}", err=True)
sys.exit(1)
# Main CLI entry point
if __name__ == '__main__':
indian_cli()