from io import BytesIO from typing import Any from PIL import Image, ImageDraw, ImageFont def render_market_chart(report: dict[str, Any]) -> bytes: image = Image.new("RGB", (1200, 720), (248, 250, 252)) draw = ImageDraw.Draw(image) title = _font(34) label = _font(22) chart = report.get("chart_data") or {} if "quotes" in chart: draw.text( (55, 35), f"Stock Analysis: {chart.get('symbol') or ''}", fill=(17, 24, 39), font=title, ) quotes = chart["quotes"] values = [float(item["close"]) for item in quotes] if values: low, high = min(values), max(values) span = high - low or 1 points = [] for index, value in enumerate(values): x = 70 + index * 1060 / max(len(values) - 1, 1) y = 610 - (value - low) * 480 / span points.append((x, y)) draw.line(points, fill=(37, 99, 235), width=5) draw.text( (70, 630), f"最低 {low:.2f} 最高 {high:.2f} 最新 {values[-1]:.2f}", fill=(75, 85, 99), font=label, ) else: draw.text((55, 35), "A-Share Market Overview", fill=(17, 24, 39), font=title) values = [ ("Advances", int(chart.get("advances") or 0)), ("Declines", int(chart.get("declines") or 0)), ("Flat", int(chart.get("flat") or 0)), ] maximum = max((value for _, value in values), default=1) or 1 for index, (name, value) in enumerate(values): y = 170 + index * 150 draw.text((70, y), f"{name} {value:,}", fill=(75, 85, 99), font=label) draw.rectangle((70, y + 45, 1120, y + 82), fill=(209, 213, 219)) draw.rectangle( (70, y + 45, 70 + 1050 * value / maximum, y + 82), fill=((37, 99, 235), (220, 38, 38), (245, 158, 11))[index], ) output = BytesIO() image.save(output, format="PNG", optimize=True) return output.getvalue() def _font(size: int) -> ImageFont.ImageFont: try: return ImageFont.truetype("DejaVuSans.ttf", size=size) except OSError: return ImageFont.load_default(size=size)