single hurst band works OK
This commit is contained in:
67
app.py
67
app.py
@ -12,7 +12,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
# --- Configuration ---
|
||||
SYMBOL = 'ETHUSDT'
|
||||
HISTORY_FILE = 'historical_data_1m.json' # Used as a cache to prevent re-downloading
|
||||
HISTORY_FILE = 'historical_data_1m.json'
|
||||
RESTART_TIMEOUT_S = 15
|
||||
BINANCE_WS_URL = f"wss://stream.binance.com:9443/ws/{SYMBOL.lower()}@trade"
|
||||
|
||||
@ -27,62 +27,48 @@ socketio = SocketIO(app, async_mode='threading')
|
||||
# --- Global State ---
|
||||
app_initialized = False
|
||||
app_init_lock = Lock()
|
||||
current_bar = {} # To track the currently forming 1-minute candle
|
||||
|
||||
# --- 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}")
|
||||
logging.info(f"Fetching chunk {i + 1}/{num_chunks} 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)
|
||||
|
||||
socketio.emit('history_progress', {'progress': ((i + 1) / num_chunks) * 100}, 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)
|
||||
unique_klines = [kline for kline in sorted(all_klines, key=lambda x: x[0]) if tuple(kline) not in seen and not seen.add(tuple(kline))]
|
||||
|
||||
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():
|
||||
"""
|
||||
Connects to Binance, manages the 1-minute candle, and emits updates.
|
||||
"""
|
||||
global current_bar
|
||||
async def listener():
|
||||
global current_bar
|
||||
while True:
|
||||
try:
|
||||
logging.info(f"Connecting to Binance WebSocket at {BINANCE_WS_URL}...")
|
||||
@ -90,7 +76,27 @@ def binance_listener_thread():
|
||||
logging.info("Binance WebSocket connected successfully.")
|
||||
while True:
|
||||
message = await websocket.recv()
|
||||
socketio.emit('trade', json.loads(message))
|
||||
trade = json.loads(message)
|
||||
|
||||
price = float(trade['p'])
|
||||
trade_time_s = trade['T'] // 1000
|
||||
candle_timestamp = trade_time_s - (trade_time_s % 60)
|
||||
|
||||
if not current_bar or candle_timestamp > current_bar.get("time", 0):
|
||||
if current_bar:
|
||||
# The previous candle is now closed, emit it
|
||||
logging.info(f"Candle closed at {current_bar['close']}. Emitting 'candle_closed' event.")
|
||||
socketio.emit('candle_closed', current_bar)
|
||||
|
||||
current_bar = {"time": candle_timestamp, "open": price, "high": price, "low": price, "close": price}
|
||||
else:
|
||||
current_bar['high'] = max(current_bar.get('high', price), price)
|
||||
current_bar['low'] = min(current_bar.get('low', price), price)
|
||||
current_bar['close'] = price
|
||||
|
||||
# Emit the live, updating candle for visual feedback
|
||||
socketio.emit('candle_update', current_bar)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Binance listener error: {e}. Reconnecting...")
|
||||
await asyncio.sleep(RESTART_TIMEOUT_S)
|
||||
@ -109,17 +115,6 @@ def handle_connect():
|
||||
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():
|
||||
|
||||
Reference in New Issue
Block a user