memex-engine / app.py
Amit9011's picture
Update app.py
4aebe9e verified
Raw
History Blame Contribute Delete
3.63 kB
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")