File size: 3,627 Bytes
4aebe9e
4e4d823
4aebe9e
 
 
 
 
 
 
 
 
 
 
4e4d823
 
 
4aebe9e
 
 
 
 
 
 
4e4d823
 
 
 
 
 
 
 
 
4aebe9e
 
4e4d823
 
4aebe9e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
90
91
92
93
94
95
96
97
98
99
import io
import os
import numpy as np
from scipy.interpolate import PchipInterpolator
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime, timezone, timedelta
from fastapi import FastAPI, Header, HTTPException
from fastapi.responses import Response
from pydantic import BaseModel
from typing import List, Tuple

app = FastAPI()

# Set this to a strong password. You will put this same password in your bot's Render dashboard.
API_SECRET = os.environ.get("API_SECRET", "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")

class ChartRequest(BaseModel):
    symbol: str
    history: List[Tuple[str, float]]
    current_price: float

def verify_request(secret: str):
    if secret != API_SECRET:
        raise HTTPException(status_code=401, detail="Unauthorized")

@app.get("/")
def health_check():
    return {"status": "alive"}

@app.post("/generate_chart")
def generate_chart(req: ChartRequest, secret: str = Header(None)):
    verify_request(secret)
    
    # Reconstruct the timestamps
    parsed_history = []
    for ts_str, price in req.history:
        dt = datetime.fromisoformat(ts_str)
        parsed_history.append((dt, price))
        
    bg_color, text_color, grid_color = '#111214', '#b5bac1', '#2b2d31'

    fig, ax = plt.subplots(figsize=(7, 3))
    fig.patch.set_facecolor(bg_color)
    ax.set_facecolor(bg_color)

    if not parsed_history:
        now = datetime.now(timezone.utc)
        timestamps = [now - timedelta(minutes=5), now]
        prices = [req.current_price, req.current_price]
    else:
        timestamps = [row[0] for row in parsed_history] + [datetime.now(timezone.utc).replace(tzinfo=None)]
        prices = [row[1] for row in parsed_history] + [req.current_price]

    unique_data = {t: p for t, p in zip(timestamps, prices)}
    sorted_times = sorted(unique_data.keys())
    sorted_prices = [unique_data[t] for t in sorted_times]

    color = '#00ff55' if sorted_prices[-1] >= sorted_prices[0] else '#ff2a2a'
    min_price, max_price = min(sorted_prices), max(sorted_prices)
    padding = max((max_price - min_price) * 0.1, req.current_price * 0.05)
    bottom_bound = max(0, min_price - padding)
    
    ax.set_ylim(bottom_bound, max_price + padding)

    if len(sorted_times) >= 3:
        date_nums = mdates.date2num(sorted_times)
        x_new = np.linspace(date_nums.min(), date_nums.max(), 300)
        spl = PchipInterpolator(date_nums, sorted_prices)
        y_smooth = spl(x_new)
        ax.plot(x_new, y_smooth, color=color, linewidth=2.5)
        ax.fill_between(x_new, y_smooth, bottom_bound, color=color, alpha=0.15)
        ax.xaxis_date()
    else:
        ax.plot(sorted_times, sorted_prices, color=color, linewidth=2.5, marker='o', markersize=4)
        ax.fill_between(sorted_times, sorted_prices, bottom_bound, color=color, alpha=0.15)

    ax.set_ylabel('Price ($)', color=text_color, fontsize=9)
    ax.tick_params(axis='x', colors=text_color, labelsize=8)
    ax.tick_params(axis='y', colors=text_color, labelsize=8)
    ax.grid(True, linestyle='--', color=grid_color, alpha=0.8)
    ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
    fig.autofmt_xdate(rotation=30)
    
    for spine in ax.spines.values():
        spine.set_color(grid_color)

    plt.title(f"{req.symbol} Price Action", color='#ffffff', fontsize=10, pad=10)
    fig.tight_layout()

    buf = io.BytesIO()
    plt.savefig(buf, format='png', dpi=120, bbox_inches='tight', facecolor=fig.get_facecolor())
    buf.seek(0)
    plt.close(fig)
    
    return Response(content=buf.read(), media_type="image/png")