File size: 3,395 Bytes
4464b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import yfinance as yf
import mplfinance as mpf
import pandas as pd
from pathlib import Path
from typing import Optional
import matplotlib
matplotlib.use('Agg') # Use non-interactive backend

def generate_multimodal_chart(ticker: str, timeframe: str = "daily", save_dir: str = "data", end_date: Optional[str] = None) -> Optional[str]:
    """

    Fetches OHLCV data for a ticker and generates a high-resolution .png candlestick chart 

    with volume and moving averages plotted for VLM analysis. Supports point-in-time data for backtesting.

    

    Args:

        ticker (str): The stock symbol (e.g., 'AAPL').

        timeframe (str): 'daily' or 'weekly'.

        save_dir (str): The directory to save the generated image.

        end_date (str, optional): The point-in-time cutoff (YYYY-MM-DD) for backtesting.

        

    Returns:

        Optional[str]: The absolute path to the saved .png file, or None if failed.

    """
    save_path = Path(save_dir)
    save_path.mkdir(parents=True, exist_ok=True)
    
    # Configure period and interval based on timeframe
    if timeframe.lower() == "weekly":
        period = "2y"
        interval = "1wk"
        file_suffix = "weekly"
    else:
        period = "6mo"
        interval = "1d"
        file_suffix = "daily"

    try:
        # Fetch data
        if end_date:
            # For backtesting, we need more control over the date range
            df = yf.download(ticker, end=end_date, period=period, interval=interval, progress=False)
        else:
            df = yf.download(ticker, period=period, interval=interval, progress=False)
        
        if df.empty:
            print(f"Error: No data found for {ticker}.")
            return None
            
        # Clean up column names for mplfinance (yfinance returns multi-index sometimes)
        if isinstance(df.columns, pd.MultiIndex):
            df.columns = df.columns.droplevel(1)
            
        # Ensure required columns exist
        required_cols = ['Open', 'High', 'Low', 'Close', 'Volume']
        if not all(col in df.columns for col in required_cols):
            print(f"Error: Missing required OHLCV columns for {ticker}.")
            return None

        # Setup plot style and parameters
        mc = mpf.make_marketcolors(up='g', down='r', edge='i', wick='i', volume='in', ohlc='i')
        s = mpf.make_mpf_style(marketcolors=mc, gridstyle=':', y_on_right=False)
        
        # Output file path
        output_file = save_path / f"{ticker.upper()}_{file_suffix}_chart.png"
        
        # Plot and save
        mpf.plot(
            df,
            type='candle',
            volume=True,
            mav=(20, 50),  # Plot 20-period and 50-period moving averages
            style=s,
            title=f"{ticker.upper()} {timeframe.capitalize()} Chart",
            ylabel='Price ($)',
            ylabel_lower='Volume',
            savefig=dict(fname=str(output_file), dpi=300, bbox_inches='tight')
        )
        
        return str(output_file.absolute())
        
    except Exception as e:
        print(f"Failed to generate chart for {ticker}: {str(e)}")
        return None

if __name__ == "__main__":
    # Test the function
    res = generate_multimodal_chart("NVDA", "daily")
    if res:
        print(f"Successfully saved chart to: {res}")