TradingAgents/tests/test_indian_market.py

401 lines
15 KiB
Python

"""
Test Suite for Indian Market Functionality
Comprehensive tests for Indian stock market features
"""
import unittest
import pytest
from datetime import datetime, timedelta
from unittest.mock import Mock, patch, MagicMock
import sys
import os
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from tradingagents.indian_config import (
get_indian_config,
get_major_stocks,
get_sector_stocks,
is_market_open,
get_market_status
)
from tradingagents.dataflows.ticker_utils import (
TickerFormatter,
TickerValidator,
TickerConverter,
TickerManager,
format_indian_ticker,
validate_indian_ticker
)
class TestIndianConfig(unittest.TestCase):
"""Test Indian market configuration"""
def test_get_indian_config(self):
"""Test getting Indian configuration"""
config = get_indian_config()
self.assertIsInstance(config, dict)
self.assertEqual(config["market_region"], "india")
self.assertEqual(config["currency"], "INR")
self.assertEqual(config["timezone"], "Asia/Kolkata")
self.assertIn("NSE", config["exchanges"]["supported"])
self.assertIn("BSE", config["exchanges"]["supported"])
def test_get_major_stocks(self):
"""Test getting major stocks list"""
stocks = get_major_stocks()
self.assertIsInstance(stocks, dict)
self.assertIn("RELIANCE", stocks)
self.assertIn("TCS", stocks)
self.assertIn("HDFCBANK", stocks)
# Check stock structure
reliance = stocks["RELIANCE"]
self.assertIn("name", reliance)
self.assertIn("sector", reliance)
self.assertIn("exchange", reliance)
def test_get_sector_stocks(self):
"""Test getting sector stocks"""
banking_stocks = get_sector_stocks("banking")
it_stocks = get_sector_stocks("it")
self.assertIsInstance(banking_stocks, list)
self.assertIsInstance(it_stocks, list)
self.assertIn("HDFCBANK", banking_stocks)
self.assertIn("TCS", it_stocks)
# Test non-existent sector
invalid_stocks = get_sector_stocks("invalid_sector")
self.assertEqual(invalid_stocks, [])
@patch('tradingagents.indian_config.datetime')
def test_market_status_functions(self, mock_datetime):
"""Test market status and timing functions"""
# Mock a weekday during market hours (Tuesday 10:00 AM IST)
mock_now = Mock()
mock_now.weekday.return_value = 1 # Tuesday
mock_now.hour = 10
mock_now.minute = 0
mock_datetime.datetime.now.return_value = mock_now
mock_datetime.datetime.now().replace.return_value = mock_now
# Test during market hours
# Note: These tests might need adjustment based on actual implementation
status = get_market_status()
self.assertIsInstance(status, str)
# Test weekend
mock_now.weekday.return_value = 5 # Saturday
status = get_market_status()
self.assertEqual(status, "closed_weekend")
class TestTickerUtils(unittest.TestCase):
"""Test ticker utilities"""
def test_ticker_formatter(self):
"""Test ticker formatting"""
# Test NSE formatting
nse_ticker = TickerFormatter.format_ticker("RELIANCE", "NSE")
self.assertEqual(nse_ticker, "RELIANCE.NS")
# Test BSE formatting
bse_ticker = TickerFormatter.format_ticker("RELIANCE", "BSE")
self.assertEqual(bse_ticker, "RELIANCE.BO")
# Test with already formatted ticker
formatted_ticker = TickerFormatter.format_ticker("TCS.NS", "NSE")
self.assertEqual(formatted_ticker, "TCS.NS")
# Test plain symbol extraction
plain = TickerFormatter.get_plain_symbol("RELIANCE.NS")
self.assertEqual(plain, "RELIANCE")
plain = TickerFormatter.get_plain_symbol("RELIANCE")
self.assertEqual(plain, "RELIANCE")
def test_ticker_validator(self):
"""Test ticker validation"""
# Test valid NSE tickers
self.assertTrue(TickerValidator.is_valid_nse_ticker("RELIANCE.NS"))
self.assertTrue(TickerValidator.is_valid_nse_ticker("TCS.NS"))
# Test valid BSE tickers
self.assertTrue(TickerValidator.is_valid_bse_ticker("500325.BO"))
self.assertTrue(TickerValidator.is_valid_bse_ticker("532540.BO"))
# Test invalid formats
self.assertFalse(TickerValidator.is_valid_nse_ticker("RELIANCE"))
self.assertFalse(TickerValidator.is_valid_bse_ticker("RELIANCE.NS"))
# Test general validation
self.assertTrue(TickerValidator.is_valid_indian_ticker("RELIANCE.NS"))
self.assertTrue(TickerValidator.is_valid_indian_ticker("500325.BO"))
self.assertFalse(TickerValidator.is_valid_indian_ticker("AAPL"))
def test_ticker_converter(self):
"""Test ticker conversion between exchanges"""
# Test NSE to BSE conversion
bse_ticker = TickerConverter.nse_to_bse("RELIANCE")
self.assertEqual(bse_ticker, "500325.BO")
bse_ticker = TickerConverter.nse_to_bse("RELIANCE.NS")
self.assertEqual(bse_ticker, "500325.BO")
# Test BSE to NSE conversion
nse_ticker = TickerConverter.bse_to_nse("500325")
self.assertEqual(nse_ticker, "RELIANCE.NS")
nse_ticker = TickerConverter.bse_to_nse("500325.BO")
self.assertEqual(nse_ticker, "RELIANCE.NS")
# Test cross-exchange conversion
cross_ticker = TickerConverter.get_cross_exchange_ticker("RELIANCE.NS")
self.assertEqual(cross_ticker, "500325.BO")
cross_ticker = TickerConverter.get_cross_exchange_ticker("500325.BO")
self.assertEqual(cross_ticker, "RELIANCE.NS")
# Test non-existent mapping
result = TickerConverter.nse_to_bse("NONEXISTENT")
self.assertIsNone(result)
def test_ticker_manager(self):
"""Test ticker manager functionality"""
manager = TickerManager()
# Test ticker processing
result = manager.process_ticker("RELIANCE", "NSE")
self.assertIsInstance(result, dict)
self.assertTrue(result["is_valid"])
self.assertEqual(result["formatted_ticker"], "RELIANCE.NS")
self.assertEqual(result["plain_symbol"], "RELIANCE")
self.assertEqual(result["exchange"], "NSE")
self.assertEqual(result["cross_exchange_ticker"], "500325.BO")
# Test all formats
formats = manager.get_all_formats("RELIANCE")
self.assertEqual(formats["plain"], "RELIANCE")
self.assertEqual(formats["nse"], "RELIANCE.NS")
self.assertEqual(formats["bse"], "RELIANCE.BO")
self.assertEqual(formats["bse_equivalent"], "500325.BO")
def test_convenience_functions(self):
"""Test convenience functions"""
# Test formatting
ticker = format_indian_ticker("RELIANCE", "NSE")
self.assertEqual(ticker, "RELIANCE.NS")
# Test validation
is_valid = validate_indian_ticker("RELIANCE.NS")
self.assertTrue(is_valid)
is_valid = validate_indian_ticker("INVALID")
self.assertFalse(is_valid)
class TestIndianMarketUtils(unittest.TestCase):
"""Test Indian market data utilities"""
@patch('tradingagents.dataflows.indian_market_utils.requests.get')
def test_alpha_vantage_api(self, mock_get):
"""Test Alpha Vantage API client"""
from tradingagents.dataflows.indian_market_utils import AlphaVantageAPI
# Mock successful response
mock_response = Mock()
mock_response.json.return_value = {
"Time Series (Daily)": {
"2024-01-01": {
"1. open": "100.0",
"2. high": "105.0",
"3. low": "99.0",
"4. close": "103.0",
"5. volume": "1000000"
}
}
}
mock_response.raise_for_status.return_value = None
mock_get.return_value = mock_response
api = AlphaVantageAPI("test_key")
# Test rate limiter initialization
self.assertIsNotNone(api.rate_limiter)
# Test data retrieval
df = api.get_daily_data("RELIANCE.NS")
self.assertIsNotNone(df)
# Additional assertions would depend on pandas DataFrame structure
@patch('yfinance.Ticker')
def test_yahoo_finance_api(self, mock_ticker):
"""Test Yahoo Finance API client"""
from tradingagents.dataflows.indian_market_utils import YahooFinanceAPI
# Mock yfinance response
mock_ticker_instance = Mock()
mock_ticker_instance.history.return_value = Mock() # Mock DataFrame
mock_ticker_instance.info = {"symbol": "RELIANCE.NS", "longName": "Reliance Industries"}
mock_ticker.return_value = mock_ticker_instance
api = YahooFinanceAPI()
# Test company info retrieval
info = api.get_company_info("RELIANCE.NS")
self.assertIsInstance(info, dict)
self.assertEqual(info["symbol"], "RELIANCE.NS")
class TestIndianAgentToolkit(unittest.TestCase):
"""Test Indian agent toolkit"""
def setUp(self):
"""Set up test fixtures"""
# Mock the toolkit to avoid actual API calls
self.toolkit_patcher = patch('tradingagents.agents.utils.indian_agent_toolkit.indian_toolkit')
self.mock_toolkit = self.toolkit_patcher.start()
def tearDown(self):
"""Clean up test fixtures"""
self.toolkit_patcher.stop()
def test_toolkit_initialization(self):
"""Test toolkit initialization"""
from tradingagents.agents.utils.indian_agent_toolkit import IndianAgentToolkit
# This will test the import and basic initialization
# Actual functionality testing would require mocking external dependencies
try:
toolkit = IndianAgentToolkit()
self.assertIsNotNone(toolkit)
except Exception as e:
# If initialization fails due to missing dependencies, that's expected in test environment
self.assertIsInstance(e, (ImportError, AttributeError))
class TestIndianAnalysts(unittest.TestCase):
"""Test Indian market analysts"""
@patch('tradingagents.agents.utils.agent_utils.AgentUtils')
def test_fundamentals_analyst_initialization(self, mock_agent_utils):
"""Test fundamentals analyst initialization"""
from tradingagents.agents.analysts.indian_fundamentals_analyst import IndianFundamentalsAnalyst
try:
analyst = IndianFundamentalsAnalyst()
self.assertEqual(analyst.agent_id, "indian_fundamentals_analyst")
self.assertIsNotNone(analyst.major_stocks)
self.assertIsInstance(analyst.key_metrics, list)
except ImportError:
# Skip if dependencies not available
self.skipTest("Dependencies not available for analyst testing")
@patch('tradingagents.agents.utils.agent_utils.AgentUtils')
def test_market_analyst_initialization(self, mock_agent_utils):
"""Test market analyst initialization"""
from tradingagents.agents.analysts.indian_market_analyst import IndianMarketAnalyst
try:
analyst = IndianMarketAnalyst()
self.assertEqual(analyst.agent_id, "indian_market_analyst")
self.assertIsNotNone(analyst.config)
self.assertIsInstance(analyst.technical_indicators, list)
except ImportError:
# Skip if dependencies not available
self.skipTest("Dependencies not available for analyst testing")
class TestIntegration(unittest.TestCase):
"""Integration tests for Indian market functionality"""
def test_end_to_end_ticker_processing(self):
"""Test end-to-end ticker processing"""
# Test the complete flow from raw symbol to formatted ticker
raw_symbol = "reliance"
# Format ticker
formatted = format_indian_ticker(raw_symbol, "NSE")
self.assertEqual(formatted, "RELIANCE.NS")
# Validate ticker
is_valid = validate_indian_ticker(formatted)
self.assertTrue(is_valid)
# Get cross-exchange equivalent
manager = TickerManager()
result = manager.process_ticker(raw_symbol, "NSE")
self.assertTrue(result["is_valid"])
self.assertEqual(result["cross_exchange_ticker"], "500325.BO")
def test_config_consistency(self):
"""Test configuration consistency across modules"""
config = get_indian_config()
major_stocks = get_major_stocks()
# Ensure major stocks are consistent with config
self.assertIsInstance(config, dict)
self.assertIsInstance(major_stocks, dict)
# Check that all major stocks have required fields
for symbol, info in major_stocks.items():
self.assertIn("name", info)
self.assertIn("sector", info)
self.assertIn("exchange", info)
self.assertIn(info["exchange"], config["exchanges"]["supported"])
class TestErrorHandling(unittest.TestCase):
"""Test error handling in Indian market functionality"""
def test_invalid_ticker_handling(self):
"""Test handling of invalid tickers"""
manager = TickerManager()
# Test with invalid symbol
result = manager.process_ticker("INVALID_SYMBOL_123", "NSE")
# Should still process but may have limitations
self.assertIsInstance(result, dict)
self.assertIn("formatted_ticker", result)
def test_invalid_sector_handling(self):
"""Test handling of invalid sectors"""
stocks = get_sector_stocks("invalid_sector")
self.assertEqual(stocks, [])
def test_api_error_handling(self):
"""Test API error handling"""
from tradingagents.dataflows.indian_market_utils import AlphaVantageAPI
# Test with invalid API key
api = AlphaVantageAPI("invalid_key")
# This should handle errors gracefully
# In a real test, we'd mock the API response to return an error
self.assertIsNotNone(api.api_key)
# Test data for mocking
SAMPLE_MARKET_DATA = """
Date,Open,High,Low,Close,Volume
2024-01-01,100.0,105.0,99.0,103.0,1000000
2024-01-02,103.0,108.0,102.0,107.0,1200000
2024-01-03,107.0,110.0,105.0,109.0,900000
"""
SAMPLE_FUNDAMENTALS_DATA = {
"symbol": "RELIANCE.NS",
"marketCap": 1500000000000,
"trailingPE": 15.5,
"priceToBook": 2.1,
"dividendYield": 0.035,
"sector": "Energy",
"industry": "Oil & Gas"
}
if __name__ == '__main__':
# Run tests
unittest.main(verbosity=2)