Files
personal_TV/app.py

132 lines
5.0 KiB
Python

import time
import logging
import asyncio
import os
import json
from flask import Flask, render_template, request
from flask_socketio import SocketIO
from binance import Client
import websockets
from threading import Lock
from datetime import datetime, timedelta
# --- Configuration ---
SYMBOL = 'ETHUSDT'
HISTORY_FILE = 'historical_data_1m.json' # Used as a cache to prevent re-downloading
RESTART_TIMEOUT_S = 15
BINANCE_WS_URL = f"wss://stream.binance.com:9443/ws/{SYMBOL.lower()}@trade"
# --- Logging Setup ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# --- Flask App Initialization ---
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode='threading')
# --- Global State ---
app_initialized = False
app_init_lock = Lock()
# --- Historical Data Streaming ---
def stream_historical_data(sid):
"""
Fetches historical data in chunks and streams it to the client with progress updates.
"""
try:
logging.info(f"Starting historical data stream for SID={sid}")
client = Client()
# Fetch the last 90 days of data in 6 chunks of 15 days each.
num_chunks = 6
chunk_size_days = 15
end_date = datetime.utcnow()
all_klines = []
for i in range(num_chunks):
start_date = end_date - timedelta(days=chunk_size_days)
logging.info(f"Fetching chunk {i + 1}/{num_chunks} ({start_date} to {end_date}) for SID={sid}")
new_klines = client.get_historical_klines(SYMBOL, Client.KLINE_INTERVAL_1MINUTE, str(start_date), str(end_date))
if new_klines:
all_klines.extend(new_klines)
progress_payload = {
'progress': ((i + 1) / num_chunks) * 100
}
socketio.emit('history_progress', progress_payload, to=sid)
end_date = start_date
socketio.sleep(0.05)
seen = set()
unique_klines = []
for kline in sorted(all_klines, key=lambda x: x[0]):
kline_tuple = tuple(kline)
if kline_tuple not in seen:
unique_klines.append(kline)
seen.add(kline_tuple)
with open(HISTORY_FILE, 'w') as f:
json.dump(unique_klines, f)
logging.info(f"Finished data stream for SID={sid}. Sending final payload of {len(unique_klines)} klines.")
socketio.emit('history_finished', {'klines_1m': unique_klines}, to=sid)
except Exception as e:
logging.error(f"Error in stream_historical_data for SID={sid}: {e}", exc_info=True)
socketio.emit('history_error', {'message': str(e)}, to=sid)
# --- Real-time Data Listener ---
def binance_listener_thread():
async def listener():
while True:
try:
logging.info(f"Connecting to Binance WebSocket at {BINANCE_WS_URL}...")
async with websockets.connect(BINANCE_WS_URL) as websocket:
logging.info("Binance WebSocket connected successfully.")
while True:
message = await websocket.recv()
socketio.emit('trade', json.loads(message))
except Exception as e:
logging.error(f"Binance listener error: {e}. Reconnecting...")
await asyncio.sleep(RESTART_TIMEOUT_S)
asyncio.run(listener())
# --- SocketIO Event Handlers ---
@socketio.on('connect')
def handle_connect():
global app_initialized
logging.info(f"Client connected: IP={request.remote_addr}, SID={request.sid}")
with app_init_lock:
if not app_initialized:
logging.info("--- Initializing Application ---")
socketio.start_background_task(binance_listener_thread)
app_initialized = True
socketio.start_background_task(target=stream_historical_data, sid=request.sid)
@socketio.on('analyze_chart')
def handle_analyze_chart(data):
sid = request.sid
logging.info(f"Received 'analyze_chart' request from frontend (SID={sid})")
recent_data = data[-100:]
prompt_data = "\n".join([f"Time: {c['time']}, Open: {c['open']}, High: {c['high']}, Low: {c['low']}, Close: {c['close']}" for c in recent_data])
prompt = (f"You are a financial analyst. Based on the following recent candlestick data for {SYMBOL}, provide a brief technical analysis (3-4 sentences). Mention the current trend and any potential short-term support or resistance levels.\n\nData:\n{prompt_data}")
socketio.emit('analysis_result', {'analysis': "AI analysis is currently unavailable."}, to=sid)
# --- Flask Routes ---
@app.route('/')
def index():
return render_template('index.html', symbol=SYMBOL)
# --- Main Application Execution ---
if __name__ == '__main__':
logging.info("Starting Flask-SocketIO server...")
socketio.run(app, host='0.0.0.0', port=5000, allow_unsafe_werkzeug=True, debug=False)