| import requests |
| import time |
| import pandas as pd |
| import gradio as gr |
| from fastapi import FastAPI |
| import threading |
|
|
| app = FastAPI() |
|
|
| URL = "https://histdatafeed.vps.com.vn/tradingview/history" |
|
|
| |
| latest_data = None |
| latest_signal = None |
| last_update = 0 |
|
|
| |
| def fetch_data(): |
| global latest_data, last_update |
|
|
| try: |
| params = { |
| "symbol": "VN30F1M", |
| "resolution": "1", |
| "from": int(time.time()) - 3600, |
| "to": int(time.time()), |
| "countback": 200 |
| } |
|
|
| res = requests.get(URL, params=params, timeout=5) |
| data = res.json() |
|
|
| df = pd.DataFrame({ |
| "time": data["t"], |
| "open": data["o"], |
| "high": data["h"], |
| "low": data["l"], |
| "close": data["c"], |
| "volume": data["v"] |
| }) |
|
|
| latest_data = df |
| last_update = time.time() |
|
|
| except Exception as e: |
| print("Fetch error:", e) |
|
|
| |
| def compute_signal(): |
| global latest_data, latest_signal |
|
|
| if latest_data is None or len(latest_data) < 20: |
| return "WAIT" |
|
|
| df = latest_data.copy() |
| df["ma20"] = df["close"].rolling(20).mean() |
|
|
| if df["close"].iloc[-1] > df["ma20"].iloc[-1]: |
| latest_signal = "BUY" |
| else: |
| latest_signal = "SELL" |
|
|
| |
| def heartbeat(): |
| while True: |
| fetch_data() |
| compute_signal() |
| print("Heartbeat update:", latest_signal) |
| time.sleep(10) |
|
|
| threading.Thread(target=heartbeat, daemon=True).start() |
|
|
| |
| @app.get("/signal") |
| def signal(): |
| return { |
| "signal": latest_signal, |
| "price": float(latest_data["close"].iloc[-1]) if latest_data is not None else 0, |
| "time": int(last_update) |
| } |
|
|
| |
| def ui(): |
| return f"Signal: {latest_signal} | Updated: {int(last_update)}" |
|
|
| interface = gr.Interface( |
| fn=ui, |
| inputs=[], |
| outputs="text", |
| live=True |
| ).queue() |
|
|
| app = gr.mount_gradio_app(app, interface, path="/") |