Erster Commit des bestehenden monolithischen WinForms-Copytraders, inklusive der Alt-Backups (*.bak), damit diese dauerhaft in der Historie rekonstruierbar bleiben. Threema-Lib unter libs/ wurde vendored (nested .git entfernt). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
162 lines
5.3 KiB
Python
162 lines
5.3 KiB
Python
import requests
|
|
import argparse
|
|
import json
|
|
import os
|
|
import statistics
|
|
import datetime
|
|
|
|
def fetch_activity_3days(wallet):
|
|
all_trades = []
|
|
offset = 0
|
|
now_ts = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
|
|
three_days = 3 * 24 * 60 * 60
|
|
|
|
print(f"Fetching 3 days history for {wallet}...")
|
|
while True:
|
|
url = f"https://data-api.polymarket.com/activity?user={wallet}&limit=1000&offset={offset}"
|
|
try:
|
|
r = requests.get(url, timeout=15)
|
|
if r.status_code == 200:
|
|
data = r.json()
|
|
items = data if isinstance(data, list) else (data.get("value", data.get("data", [])) if isinstance(data, dict) else [])
|
|
|
|
if not items:
|
|
break
|
|
|
|
all_trades.extend(items)
|
|
|
|
# Check if we have passed 3 days
|
|
oldest_ts = items[-1].get("timestamp")
|
|
if oldest_ts and (now_ts - oldest_ts) >= three_days:
|
|
break
|
|
|
|
offset += 1000
|
|
else:
|
|
break
|
|
except Exception as e:
|
|
print(f"Error fetching {wallet}: {e}")
|
|
break
|
|
|
|
return all_trades
|
|
|
|
def analyze_trader(wallet, display_name):
|
|
trades = fetch_activity_3days(wallet)
|
|
if not trades:
|
|
return None
|
|
|
|
# Filter trades and sort ascending (oldest first)
|
|
valid_trades = [t for t in trades if t.get("type") == "TRADE" and t.get("timestamp") and t.get("asset")]
|
|
valid_trades.sort(key=lambda x: x["timestamp"])
|
|
|
|
# Group by asset
|
|
from collections import defaultdict
|
|
by_asset = defaultdict(list)
|
|
for t in valid_trades:
|
|
by_asset[t["asset"]].append(t)
|
|
|
|
total_evaluated = 0
|
|
snipes = 0
|
|
hold_times = []
|
|
|
|
for asset, asset_trades in by_asset.items():
|
|
# Find first BUY
|
|
buy_ts = None
|
|
for t in asset_trades:
|
|
if t["side"] == "BUY":
|
|
buy_ts = t["timestamp"]
|
|
break
|
|
|
|
if buy_ts is None:
|
|
continue
|
|
|
|
# Find first SELL after BUY (allow same second for immediate script-sells)
|
|
sell_ts = None
|
|
for t in asset_trades:
|
|
if t["side"] == "SELL" and t["timestamp"] >= buy_ts:
|
|
sell_ts = t["timestamp"]
|
|
break
|
|
|
|
if sell_ts is not None:
|
|
total_evaluated += 1
|
|
hold_dur = sell_ts - buy_ts
|
|
hold_times.append(hold_dur)
|
|
if hold_dur < 300: # Less than 5 minutes
|
|
snipes += 1
|
|
|
|
if total_evaluated == 0:
|
|
return {
|
|
"name": display_name,
|
|
"wallet": wallet,
|
|
"evaluated": 0,
|
|
"snipes": 0,
|
|
"ratio": 0.0,
|
|
"median": 0
|
|
}
|
|
|
|
ratio = (snipes / total_evaluated) * 100
|
|
median_hold = statistics.median(hold_times) if hold_times else 0
|
|
|
|
return {
|
|
"name": display_name,
|
|
"wallet": wallet,
|
|
"evaluated": total_evaluated,
|
|
"snipes": snipes,
|
|
"ratio": ratio,
|
|
"median": median_hold
|
|
}
|
|
|
|
def print_result(res):
|
|
print(f"Trader: {res['name']} ({res['wallet']})")
|
|
print(f" Evaluated Pairs: {res['evaluated']}")
|
|
print(f" Snipe Trades (<5m): {res['snipes']}")
|
|
if res['evaluated'] > 0:
|
|
print(f" Sniper Ratio: {res['ratio']:.2f}%")
|
|
print(f" Median Hold: {res['median']:.0f} seconds")
|
|
print("-" * 40)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Analyze a trader for Liquidity Sniping.")
|
|
parser.add_argument("--wallet", type=str, help="Single wallet to analyze")
|
|
parser.add_argument("--all", action="store_true", help="Analyze all active traders in PolyTraderDB.trackers.json")
|
|
args = parser.parse_args()
|
|
|
|
if args.wallet:
|
|
res = analyze_trader(args.wallet, "CLI_TEST")
|
|
if res:
|
|
print_result(res)
|
|
elif args.all:
|
|
print("Analyzing all active traders...")
|
|
db_path = r"bin\Debug\net8.0-windows7.0\Logs\PolyTraderDB.trackers.json"
|
|
if not os.path.exists(db_path):
|
|
print(f"Could not find DB at {db_path}")
|
|
return
|
|
|
|
with open(db_path, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
active_traders = [t for t in data if t.get("IsActive")]
|
|
print(f"Found {len(active_traders)} active traders.")
|
|
|
|
results = []
|
|
for t in active_traders:
|
|
wallet = t.get("WalletAddress")
|
|
name = t.get("DisplayName")
|
|
res = analyze_trader(wallet, name)
|
|
if res:
|
|
results.append(res)
|
|
|
|
# Sort by worst offenders (highest sniper ratio)
|
|
results.sort(key=lambda x: x["ratio"], reverse=True)
|
|
|
|
print("\n=== SNIPING REPORT ===")
|
|
print(f"{'Trader Name':<20} | {'Evaluated':<10} | {'Snipes':<8} | {'Ratio':<8} | {'Median Hold':<12}")
|
|
print("-" * 75)
|
|
for r in results:
|
|
if r['evaluated'] > 0:
|
|
print(f"{r['name']:<20} | {r['evaluated']:<10} | {r['snipes']:<8} | {r['ratio']:>5.1f}% | {r['median']:>5.0f} sec")
|
|
else:
|
|
print(f"{r['name']:<20} | {r['evaluated']:<10} | {r['snipes']:<8} | {'N/A':<8} | {'N/A':<12}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|