To handle high-volume executions without timing out TradingView's webhook limits (TradingView expects a 200 OK response within 3 seconds) or hitting broker rate limits, the Flask application must decouple the incoming request from the execution logic.
The most robust way to achieve this in a standalone Python application is by implementing a Producer-Consumer pattern using a thread-safe in-memory queue. The Flask route acts as the producer, mathematically slicing the massive position and instantly pushing the chunks to a queue. A background daemon thread acts as the consumer, popping chunks off the queue and executing them with a built-in rate-limiting delay.
The Python Flask Receiver
Code: Select all
from flask import Flask, request, jsonify
import threading
import queue
import time
import math
import logging
# Configure logging for server monitoring
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
app = Flask(__name__)
# Thread-safe queue to hold order chunks
order_queue = queue.Queue()
# --- Configuration ---
MAX_CHUNK_LOTS = 50.0
RATE_LIMIT_DELAY = 0.5 # Seconds to wait between broker API calls
MIN_LOT_STEP = 0.01 # Broker minimum volume step
#+------------------------------------------------------------------+
#| Background Worker: Processes chunks sequentially |
#+------------------------------------------------------------------+
def broker_execution_worker():
logging.info("Background broker execution worker started.")
while True:
# Block until a chunk is available in the queue
chunk = order_queue.get()
symbol = chunk.get('symbol')
qty = chunk.get('qty')
action = chunk.get('action')
try:
# ---------------------------------------------------------
# TODO: Insert your specific Broker API execution call here
# Example: requests.post('http://broker-api...', json=chunk)
# ---------------------------------------------------------
logging.info(f"EXECUTING BROKER API: {action} {qty} lots of {symbol}")
except Exception as e:
logging.error(f"Broker API Error for {symbol}: {e}")
# Optional: Implement retry logic and push back to queue on failure
finally:
# Enforce rate limit delay before processing the next chunk
time.sleep(RATE_LIMIT_DELAY)
order_queue.task_done()
# Start the background worker thread (daemon=True ensures it dies when Flask stops)
threading.Thread(target=broker_execution_worker, daemon=True).start()
#+------------------------------------------------------------------+
#| Webhook Route: Catches TV payload and chunks it instantly |
#+------------------------------------------------------------------+
@app.route('/tv-webhook', methods=['POST'])
def tradingview_webhook():
if not request.is_json:
return jsonify({"error": "Payload must be JSON"}), 400
data = request.json
symbol = data.get('symbol')
total_qty = data.get('qty')
action = data.get('action', 'close')
if not symbol or not total_qty:
return jsonify({"error": "Missing symbol or qty"}), 400
try:
total_qty = float(total_qty)
remaining_qty = abs(total_qty)
chunks_queued = 0
# Slice the massive position into broker-safe chunks
while remaining_qty >= MIN_LOT_STEP:
close_qty = min(remaining_qty, MAX_CHUNK_LOTS)
# Normalize to avoid Invalid Volume errors
close_qty = math.floor(close_qty / MIN_LOT_STEP) * MIN_LOT_STEP
if close_qty < MIN_LOT_STEP:
break
# Create the chunk payload
chunk_payload = {
"action": action,
"symbol": symbol,
"qty": round(close_qty, 2)
}
# Push to the background queue instantly
order_queue.put(chunk_payload)
remaining_qty -= close_qty
chunks_queued += 1
logging.info(f"Received webhook for {symbol}. Sliced into {chunks_queued} chunks.")
# Return 202 Accepted immediately so TradingView does not timeout
return jsonify({
"status": "queued",
"symbol": symbol,
"chunks_generated": chunks_queued
}), 202
except ValueError:
return jsonify({"error": "Invalid quantity format"}), 400
if __name__ == '__main__':
# Run the server on port 5000
app.run(host='0.0.0.0', port=5000)