Skip to content

Commit 7bacc79

Browse files
new scoring system
1 parent ca4ab40 commit 7bacc79

File tree

1 file changed

+141
-23
lines changed

1 file changed

+141
-23
lines changed

Trading-Automation/trading_automation.py

Lines changed: 141 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,29 @@ async def telegram_handle_message(update: Update, context: ContextTypes.DEFAULT_
939939
else:
940940
await send_with_keyboard(update, f"```\nSymbol Debug Info:\n{debug_text}\n```", parse_mode='Markdown')
941941

942+
elif text == "⚡ Fast Check":
943+
await send_with_keyboard(update, "⚡ Running fast momentum check for immediate opportunities...")
944+
945+
try:
946+
fetch_usdc_balance()
947+
investable = balance['usd'] - sum(float(tr.get('Tax', 0)) for tr in trade_log[-20:] if float(tr.get('Tax', 0)) > 0)
948+
949+
if investable < 10:
950+
await send_with_keyboard(update, "❌ Not enough USDC for fast investment check")
951+
return
952+
953+
# Run fast momentum check
954+
start_time = time.time()
955+
fast_momentum_invest(investable)
956+
end_time = time.time()
957+
958+
processing_time = end_time - start_time
959+
await send_with_keyboard(update,
960+
f"✅ Fast momentum check completed in {processing_time:.2f} seconds\n"
961+
f"💰 Checked ${investable:.2f} USDC for immediate opportunities")
962+
except Exception as e:
963+
await send_with_keyboard(update, f"❌ Fast check failed: {str(e)}")
964+
942965
else:
943966
await send_with_keyboard(update, "Unknown action.")
944967

@@ -982,7 +1005,8 @@ def is_paused():
9821005
["⏸ Pause Trading", "▶️ Resume Trading"],
9831006
["📝 Trade Log", "🔍 Diagnose"],
9841007
["🔄 WebSocket Status", "⚡ Check Momentum"],
985-
["🔍 Debug Symbols", "📈 Status"]
1008+
["🔍 Debug Symbols", "⚡ Fast Check"],
1009+
["📈 Status"]
9861010
]
9871011

9881012
async def start_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
@@ -1012,11 +1036,11 @@ def telegram_main():
10121036
application.add_handler(CommandHandler('start', start_handler))
10131037
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, telegram_handle_message))
10141038

1015-
# Add periodic alarm job with more conservative timing to avoid trading loop conflicts
1039+
# Add periodic alarm job with faster momentum checking
10161040
application.job_queue.run_repeating(
10171041
alarm_job,
1018-
interval=360, # 6 minutes instead of 5 to reduce conflicts
1019-
first=30, # First run after 30 seconds to let trading loop start
1042+
interval=120, # 2 minutes instead of 6 for faster response
1043+
first=15, # First run after 15 seconds for quicker startup
10201044
name="momentum_checker"
10211045
)
10221046

@@ -1446,15 +1470,81 @@ def position_profit(sym):
14461470
print("[TAXES] Not enough USDC to invest after reserving for taxes.")
14471471
return
14481472

1449-
invest_momentum_with_usdc_limit(investable_usdc)
1473+
# FAST CHECK: Look for immediate momentum opportunities
1474+
fast_momentum_invest(investable_usdc)
1475+
1476+
def fast_momentum_invest(usdc_limit):
1477+
"""
1478+
Fast momentum check that runs investment logic immediately when strong signals are detected.
1479+
This bypasses the normal periodic checks for ultra-fast response.
1480+
"""
1481+
print(f"[FAST CHECK] Looking for immediate momentum opportunities with ${usdc_limit:.2f}")
1482+
1483+
symbols = get_yaml_ranked_momentum(limit=5) # Check top 5 instead of 10 for speed
1484+
1485+
for symbol in symbols:
1486+
try:
1487+
# Quick momentum check
1488+
d1, d5, d15 = dynamic_momentum_set(symbol)
1489+
if not has_recent_momentum(symbol, d1, d5, d15):
1490+
continue
1491+
1492+
# Fast tradability check
1493+
ok, reason, diag = guard_tradability(symbol, side="BUY")
1494+
if not ok:
1495+
print(f"[FAST SKIP] {symbol}: {reason}")
1496+
continue
1497+
1498+
# Check if we have enough funds
1499+
min_notional = min_notional_for(symbol)
1500+
if usdc_limit < min_notional:
1501+
print(f"[FAST SKIP] {symbol}: Need ${min_notional:.2f}, have ${usdc_limit:.2f}")
1502+
break
1503+
1504+
# Execute immediate buy
1505+
print(f"[FAST BUY] {symbol}: Strong momentum detected, buying immediately!")
1506+
result = buy(symbol, amount=min_notional)
1507+
1508+
if result:
1509+
usdc_limit -= min_notional
1510+
print(f"[FAST SUCCESS] {symbol}: Bought ${min_notional:.2f}, remaining: ${usdc_limit:.2f}")
1511+
1512+
# Send fast alert
1513+
try:
1514+
asyncio.create_task(send_alarm_message(
1515+
f"⚡ FAST MOMENTUM BUY ⚡\n\n"
1516+
f"📊 {symbol}\n"
1517+
f"💵 Amount: ${min_notional:.2f}\n"
1518+
f"🚀 Reason: Strong 3-timeframe momentum\n"
1519+
f"⏱️ Response: Ultra-fast execution"
1520+
))
1521+
except Exception as e:
1522+
print(f"[FAST ALERT ERROR] {e}")
1523+
else:
1524+
print(f"[FAST FAIL] {symbol}: Buy failed")
1525+
1526+
except Exception as e:
1527+
print(f"[FAST ERROR] {symbol}: {e}")
1528+
continue
1529+
1530+
# If we still have funds, run normal investment logic
1531+
if usdc_limit >= 10:
1532+
invest_momentum_with_usdc_limit(usdc_limit)
14501533

14511534
def load_symbol_stats():
1535+
"""Load symbol statistics from YAML file, filtered to allowed symbols only."""
14521536
try:
14531537
with open(YAML_SYMBOLS_FILE, "r") as f:
1454-
return yaml.safe_load(f)
1538+
all_stats = yaml.safe_load(f)
1539+
# Filter to only allowed symbols
1540+
filtered_stats = {k: v for k, v in all_stats.items() if k in ALLOWED_SYMBOLS}
1541+
print(f"[YAML] Loaded {len(filtered_stats)} allowed symbols from {len(all_stats)} total")
1542+
return filtered_stats
14551543
except Exception as e:
14561544
print(f"[YAML ERROR] Could not read {YAML_SYMBOLS_FILE}: {e}")
1457-
return {}
1545+
# Return minimal stats for allowed symbols if YAML fails
1546+
return {symbol: {"market_cap": 1000000, "volume_1d": 1000000, "volatility": {"1d": 0.01}}
1547+
for symbol in ALLOWED_SYMBOLS}
14581548

14591549
def pct_change(klines):
14601550
if len(klines) < 2: return 0
@@ -1653,17 +1743,17 @@ def dynamic_momentum_threshold(symbol, interval='1m', lookback=60,
16531743

16541744
def dynamic_momentum_set(symbol):
16551745
"""
1656-
Produce per-interval dynamic thresholds tuned for micro-scalping.
1657-
We make shorter TFs a bit more sensitive (lower k), longer TFs stricter (higher k).
1746+
Produce per-interval dynamic thresholds tuned for faster micro-scalping.
1747+
We make shorter TFs more sensitive (lower k), longer TFs slightly stricter.
16581748
"""
1659-
thr_1m = dynamic_momentum_threshold(symbol, '1m', lookback=60, k=0.55, floor_=0.0007, cap_=0.015)
1660-
thr_5m = dynamic_momentum_threshold(symbol, '5m', lookback=48, k=0.70, floor_=0.0010, cap_=0.018)
1661-
thr_15m = dynamic_momentum_threshold(symbol, '15m', lookback=48, k=0.85, floor_=0.0015, cap_=0.020)
1749+
thr_1m = dynamic_momentum_threshold(symbol, '1m', lookback=60, k=0.35, floor_=0.0005, cap_=0.012) # More sensitive
1750+
thr_5m = dynamic_momentum_threshold(symbol, '5m', lookback=48, k=0.50, floor_=0.0008, cap_=0.015) # More sensitive
1751+
thr_15m = dynamic_momentum_threshold(symbol, '15m', lookback=48, k=0.65, floor_=0.0012, cap_=0.018) # More sensitive
16621752
return thr_1m, thr_5m, thr_15m
16631753

16641754

16651755
def has_recent_momentum(symbol, min_1m=None, min_5m=None, min_15m=None):
1666-
"""Enhanced momentum detection using multiple indicators with dynamic thresholds."""
1756+
"""Enhanced momentum detection using multiple indicators with faster scoring system."""
16671757
try:
16681758
# derive dynamic thresholds if not provided
16691759
if min_1m is None or min_5m is None or min_15m is None:
@@ -1685,8 +1775,39 @@ def has_recent_momentum(symbol, min_1m=None, min_5m=None, min_15m=None):
16851775
f"RSI: {indicators_15m.get('rsi', 0):.1f} Vol: {indicators_15m.get('volume_spike', False)} "
16861776
f"MA: {indicators_15m.get('ma_cross', False)}")
16871777

1688-
# Keep your strong confirmation rule (all TFs) for now; you can relax if fills are sparse
1689-
return momentum_1m and momentum_5m and momentum_15m
1778+
# NEW: Weighted scoring system for faster entry
1779+
# Score each timeframe (0-3 points each)
1780+
score_1m = sum([
1781+
indicators_1m.get('price_momentum', False), # 1 point
1782+
indicators_1m.get('volume_spike', False), # 1 point
1783+
indicators_1m.get('rsi_momentum', False) or indicators_1m.get('ma_cross', False) # 1 point
1784+
])
1785+
1786+
score_5m = sum([
1787+
indicators_5m.get('price_momentum', False), # 1 point
1788+
indicators_5m.get('volume_spike', False), # 1 point
1789+
indicators_5m.get('rsi_momentum', False) or indicators_5m.get('ma_cross', False) # 1 point
1790+
])
1791+
1792+
score_15m = sum([
1793+
indicators_15m.get('price_momentum', False), # 1 point
1794+
indicators_15m.get('volume_spike', False), # 1 point
1795+
indicators_15m.get('rsi_momentum', False) or indicators_15m.get('ma_cross', False) # 1 point
1796+
])
1797+
1798+
total_score = score_1m + score_5m + score_15m
1799+
1800+
print(f"[MOMENTUM SCORE] {symbol}: 1m={score_1m}/3, 5m={score_5m}/3, 15m={score_15m}/3, Total={total_score}/9")
1801+
1802+
# FAST MODE: Require at least 6/9 points with 1m showing strong signal
1803+
# This allows investment when momentum is building, not just when perfect
1804+
fast_entry = (total_score >= 6 and score_1m >= 2)
1805+
1806+
# CONSERVATIVE MODE: Original requirement (all timeframes perfect)
1807+
conservative_entry = momentum_1m and momentum_5m and momentum_15m
1808+
1809+
# Use FAST MODE for quicker entries
1810+
return fast_entry
16901811

16911812
except Exception as e:
16921813
print(f"[MOMENTUM ERROR] {symbol}: {e}")
@@ -1777,17 +1898,13 @@ def get_yaml_ranked_momentum(
17771898
)
17781899
return [x["symbol"] for x in ranked[:limit]]
17791900

1780-
def refresh_symbols():
1781-
global SYMBOLS
1782-
SYMBOLS = get_yaml_ranked_momentum(limit=10)
1783-
17841901
def invest_momentum_with_usdc_limit(usdc_limit):
17851902
"""
17861903
Invest in as many eligible momentum symbols as possible, always using the min_notional per symbol,
17871904
never all-or-nothing. Any remaining funds are left in USDC.
17881905
This version treats coins you own (including dust) and coins you don't equally.
17891906
"""
1790-
refresh_symbols()
1907+
# Get symbols directly without redundant refresh_symbols() call
17911908
symbols = get_yaml_ranked_momentum(limit=10)
17921909
print(f"[DEBUG] Momentum symbols eligible for investment: {symbols}")
17931910
if not symbols:
@@ -2045,8 +2162,8 @@ def process_actions():
20452162
def trading_loop():
20462163
last_sync = time.time()
20472164
last_websocket_update = time.time()
2048-
SYNC_INTERVAL = 240 # Increased from 180 to 240 seconds (4 minutes)
2049-
WEBSOCKET_UPDATE_INTERVAL = 600 # Increased to 10 minutes
2165+
SYNC_INTERVAL = 60 # Reduced from 240 to 60 seconds (1 minute) for faster response
2166+
WEBSOCKET_UPDATE_INTERVAL = 300 # Reduced to 5 minutes
20502167

20512168
# Initialize WebSocket monitoring
20522169
initialize_websocket_monitoring()
@@ -2410,7 +2527,8 @@ async def send_quick_alerts():
24102527
# Add performance monitoring to reduce scheduler warning noise
24112528
add_performance_monitoring()
24122529

2413-
refresh_symbols()
2530+
# Load trade history and rebuild positions - no need for refresh_symbols()
2531+
# since get_yaml_ranked_momentum() loads symbols dynamically when needed
24142532
trade_log = load_trade_history()
24152533
positions.clear()
24162534
positions.update(rebuild_cost_basis(trade_log))

0 commit comments

Comments
 (0)