Add crypto signals pipeline + Polymarket arb scanner

- Signal parser for Telegram JSON exports
- Price fetcher using Binance US API
- Backtester with fee-aware simulation
- Polymarket 15-min arb scanner with orderbook checking
- Systemd timer every 2 min for arb alerts
- Paper trade tracking
- Investigation: polymarket-15min-arb.md
This commit is contained in:
2026-02-09 14:31:51 -06:00
parent b24d0e87de
commit be0315894e
8 changed files with 925 additions and 2 deletions

View File

@ -0,0 +1,166 @@
#!/usr/bin/env python3
"""
Telegram Crypto Signal Parser
Parses exported Telegram JSON chat history and extracts structured trading signals.
"""
import json
import re
import sys
from datetime import datetime
from pathlib import Path
# Signal patterns - adapt as we see more formats
PATTERNS = {
# #TICKER direction entry SL target leverage balance%
'standard': re.compile(
r'#(\w+)\s+' # ticker
r'(Long|Short)\s+' # direction
r'(?:market\s+entry!?|entry[:\s]+([0-9.]+))\s*' # entry type/price
r'SL[;:\s]+([0-9.]+)\s*' # stop loss
r'(?:Targets?|TP)[;:\s]+([0-9.,\s]+)\s*' # targets (can be multiple)
r'(?:Lev(?:erage)?[:\s]*x?([0-9.]+))?\s*' # leverage (optional)
r'(?:([0-9.]+)%?\s*balance)?', # balance % (optional)
re.IGNORECASE
),
# Simpler: #TICKER Short/Long entry SL targets
'simple': re.compile(
r'#(\w+)\s+(Long|Short)',
re.IGNORECASE
),
}
def parse_signal_text(text):
"""Parse a single message text into structured signal(s)."""
signals = []
# Try to find all ticker mentions
ticker_blocks = re.split(r'(?=#\w+USDT)', text)
for block in ticker_blocks:
if not block.strip():
continue
signal = {}
# Extract ticker
ticker_match = re.search(r'#(\w+)', block)
if not ticker_match:
continue
signal['ticker'] = ticker_match.group(1).upper()
# Extract direction
dir_match = re.search(r'\b(Long|Short)\b', block, re.IGNORECASE)
if not dir_match:
continue
signal['direction'] = dir_match.group(1).lower()
# Extract entry price (or "market")
entry_match = re.search(r'(?:entry|enter)[:\s]*([0-9.]+)', block, re.IGNORECASE)
if entry_match:
signal['entry'] = float(entry_match.group(1))
else:
signal['entry'] = 'market'
# Extract stop loss
sl_match = re.search(r'SL[;:\s]+([0-9.]+)', block, re.IGNORECASE)
if sl_match:
signal['stop_loss'] = float(sl_match.group(1))
# Extract targets (can be multiple, comma or space separated)
tp_match = re.search(r'(?:Targets?|TP)[;:\s]+([0-9.,\s]+)', block, re.IGNORECASE)
if tp_match:
targets_str = tp_match.group(1)
targets = [float(t.strip()) for t in re.findall(r'[0-9.]+', targets_str)]
signal['targets'] = targets
# Extract leverage
lev_match = re.search(r'Lev(?:erage)?[:\s]*x?([0-9.]+)', block, re.IGNORECASE)
if lev_match:
signal['leverage'] = float(lev_match.group(1))
# Extract balance percentage
bal_match = re.search(r'([0-9.]+)%?\s*balance', block, re.IGNORECASE)
if bal_match:
signal['balance_pct'] = float(bal_match.group(1))
if signal.get('ticker') and signal.get('direction'):
signals.append(signal)
return signals
def parse_telegram_export(json_path):
"""Parse a Telegram JSON export file."""
with open(json_path, 'r') as f:
data = json.load(f)
messages = data.get('messages', [])
all_signals = []
for msg in messages:
if msg.get('type') != 'message':
continue
# Get text content (can be string or list of text entities)
text_parts = msg.get('text', '')
if isinstance(text_parts, list):
text = ''.join(
p if isinstance(p, str) else p.get('text', '')
for p in text_parts
)
else:
text = text_parts
if not text or '#' not in text:
continue
# Check if it looks like a signal
if not re.search(r'(Long|Short)', text, re.IGNORECASE):
continue
signals = parse_signal_text(text)
for signal in signals:
signal['timestamp'] = msg.get('date', '')
signal['message_id'] = msg.get('id', '')
signal['raw_text'] = text[:500]
all_signals.append(signal)
return all_signals
def parse_forwarded_messages(messages_text):
"""Parse signals from forwarded message text (copy-pasted or forwarded to bot)."""
signals = parse_signal_text(messages_text)
return signals
if __name__ == '__main__':
if len(sys.argv) < 2:
# Demo with the test signals
test_text = """#ASTERUSDT Short market entry! SL: 0.6385 Targets: 0.51 Lev x15 1.3% balance
#HYPEUSDT Short market entry! SL; 33.5 Target 25 Lev x12 1.4% balance"""
signals = parse_signal_text(test_text)
print(f"Parsed {len(signals)} signals:\n")
for s in signals:
print(json.dumps(s, indent=2))
else:
json_path = sys.argv[1]
signals = parse_telegram_export(json_path)
print(f"Parsed {len(signals)} signals from export\n")
# Save to output
out_path = json_path.replace('.json', '_signals.json')
with open(out_path, 'w') as f:
json.dump(signals, f, indent=2)
print(f"Saved to {out_path}")
# Quick summary
longs = sum(1 for s in signals if s['direction'] == 'long')
shorts = sum(1 for s in signals if s['direction'] == 'short')
print(f"Longs: {longs}, Shorts: {shorts}")
tickers = set(s['ticker'] for s in signals)
print(f"Unique tickers: {len(tickers)}")