feat: Manual trades wait for fresh 1-minute ATR datapoint

PHASE 2 ENHANCED: Manual trades now wait for next 1-minute datapoint
instead of using cached/stale data. Guarantees fresh ATR (<60s old).

User requirement: 'when i send a telegram message to enter the market,
the bot will simply wait for the next 1 minute datapoint'

Implementation:
- Add wait_for_fresh_market_data() async helper function
- Polls market data cache every 5 seconds (max 60s)
- Detects fresh data by timestamp change
- Extracts real ATR/ADX/RSI from 1-minute TradingView data
- User sees waiting message + confirmation when fresh data arrives
- Falls back to preset ATR 0.43 on timeout (fail-safe)

Benefits:
- Adaptive targets match CURRENT volatility (not historical)
- No stale data risk (guaranteed <60s old)
- Better than Phase 2 v1 (5-minute tolerance)
- Consistent with automated trades (same 1-min data source)

User Experience:
1. User: /long sol
2. Bot:  Waiting for next 1-minute datapoint...
3. [Wait 15-45 seconds typically]
4. Bot:  Fresh ATR: 0.4523 | ADX: 34.2 | RSI: 56.8
5. Bot:  Position opened with adaptive targets

Changes:
- Add asyncio import for async sleep
- Add wait_for_fresh_market_data() before manual_trade_handler
- Replace Phase 2 v1 (5min tolerance) with polling logic
- Add 3 user messages (waiting, confirmation, timeout)
- Extract ATR/ADX/RSI from fresh data or fallback

Files:
- telegram_command_bot.py: +70 lines polling logic
This commit is contained in:
mindesbunister
2025-12-02 19:35:24 +01:00
parent 702ef7953b
commit 23277b7c87

View File

@@ -5,6 +5,7 @@ Only responds to YOUR commands in YOUR chat
"""
import os
import time
import asyncio
import requests
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters
@@ -556,6 +557,71 @@ async def trade_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(f"❌ Error: {str(e)}")
async def wait_for_fresh_market_data(symbol: str, max_wait: int = 60):
"""
Poll market data cache until fresh data arrives (new timestamp detected).
Args:
symbol: Drift symbol (e.g., 'SOL-PERP', 'ETH-PERP')
max_wait: Maximum seconds to wait (default 60)
Returns:
dict: Fresh market data with atr/adx/rsi/timestamp
None: Timeout or error
"""
start_time = time.time()
last_timestamp = None
poll_count = 0
print(f"⏳ Waiting for fresh 1-minute data: {symbol} (max {max_wait}s)", flush=True)
while (time.time() - start_time) < max_wait:
try:
response = requests.get(
f"{TRADING_BOT_URL}/api/trading/market-data",
timeout=5
)
if response.ok:
data = response.json()
if data.get('success') and data.get('cache'):
symbol_data = data['cache'].get(symbol)
if symbol_data:
current_timestamp = symbol_data.get('timestamp')
data_age = symbol_data.get('ageSeconds', 999)
poll_count += 1
print(f"🔍 Poll #{poll_count}: timestamp={current_timestamp}, age={data_age}s", flush=True)
# Fresh data detected (timestamp changed from last poll)
if last_timestamp and current_timestamp != last_timestamp:
print(f"✅ Fresh data detected after {poll_count} polls ({time.time() - start_time:.1f}s)", flush=True)
return symbol_data
last_timestamp = current_timestamp
else:
print(f"⚠️ No data for {symbol} in cache (poll #{poll_count + 1})", flush=True)
poll_count += 1
else:
print(f"⚠️ No cache data in response (poll #{poll_count + 1})", flush=True)
poll_count += 1
else:
print(f"⚠️ Market data fetch failed: {response.status_code} (poll #{poll_count + 1})", flush=True)
poll_count += 1
except Exception as e:
print(f"⚠️ Market data poll error: {e} (poll #{poll_count + 1})", flush=True)
poll_count += 1
# Wait 5 seconds before next poll
await asyncio.sleep(5)
print(f"❌ Timeout after {poll_count} polls ({max_wait}s) - no fresh data received", flush=True)
return None
async def manual_trade_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Execute manual long/short commands sent as plain text with analytics validation."""
@@ -655,15 +721,74 @@ async def manual_trade_handler(update: Update, context: ContextTypes.DEFAULT_TYP
# Analytics check error - proceed with trade (fail-open)
print(f"⚠️ Analytics error: {analytics_error} - proceeding anyway", flush=True)
# Execute the trade
metrics = MANUAL_METRICS[direction]
# 🆕 PHASE 2 ENHANCED: Wait for fresh 1-minute datapoint (Dec 2, 2025)
# User requirement: "when i send a telegram message to enter the market,
# the bot will simply wait for the next 1 minute datapoint"
# Send waiting message to user
await update.message.reply_text(
f"⏳ *Waiting for next 1-minute datapoint...*\n"
f"Will execute with fresh ATR (max 60s)",
parse_mode='Markdown'
)
# Poll for fresh data (new timestamp = new datapoint arrived)
fresh_data = await wait_for_fresh_market_data(drift_symbol, max_wait=60)
# Extract metrics from fresh data or fallback to preset
metrics = MANUAL_METRICS[direction] # Start with preset defaults
if fresh_data:
# Use real-time metrics from fresh 1-minute data
fresh_atr = fresh_data.get('atr')
fresh_adx = fresh_data.get('adx')
fresh_rsi = fresh_data.get('rsi')
data_age = fresh_data.get('ageSeconds', 0)
if fresh_atr and fresh_atr > 0:
metrics = {
'atr': fresh_atr,
'adx': fresh_adx if fresh_adx else metrics['adx'], # Fallback if missing
'rsi': fresh_rsi if fresh_rsi else metrics['rsi'], # Fallback if missing
'volumeRatio': metrics['volumeRatio'], # Keep preset (not in 1-min data)
'pricePosition': metrics['pricePosition'], # Keep preset (not in 1-min data)
}
print(f"✅ Using fresh metrics: ATR={metrics['atr']:.4f}, ADX={metrics['adx']:.1f}, RSI={metrics['rsi']:.1f} ({data_age}s old)", flush=True)
await update.message.reply_text(
f"✅ *Fresh data received*\n"
f"ATR: {metrics['atr']:.4f} | ADX: {metrics['adx']:.1f} | RSI: {metrics['rsi']:.1f}\n"
f"Executing {direction.upper()} {symbol_info['label']}...",
parse_mode='Markdown'
)
else:
print(f"⚠️ Fresh data invalid (ATR={fresh_atr}), using preset metrics", flush=True)
await update.message.reply_text(
f"⚠️ *Fresh data invalid*\n"
f"Using preset ATR: {metrics['atr']}\n"
f"Executing {direction.upper()} {symbol_info['label']}...",
parse_mode='Markdown'
)
else:
# Timeout - fallback to preset with warning
print(f"⚠️ Timeout waiting for fresh data - using preset metrics: ATR={metrics['atr']}", flush=True)
await update.message.reply_text(
f"⚠️ *Timeout waiting for fresh data*\n"
f"Using preset ATR: {metrics['atr']}\n"
f"Executing {direction.upper()} {symbol_info['label']}...",
parse_mode='Markdown'
)
# Execute the trade with fresh or fallback metrics
payload = {
'symbol': symbol_info['tradingview'],
'direction': direction,
'timeframe': 'manual',
'signalStrength': 'manual',
'atr': metrics['atr'],
'atr': metrics['atr'], # 🆕 Fresh ATR from 1-minute data or preset fallback
'adx': metrics['adx'],
'rsi': metrics['rsi'],
'volumeRatio': metrics['volumeRatio'],