228 lines
7.9 KiB
Python
228 lines
7.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Deep distillation: reduce profitable runs to essential attack pattern.
|
||
|
|
|
||
|
|
Strategy: Track cumulative net position and floor movement.
|
||
|
|
The "attack" is the net effect: how much ETH in, how much KRK accumulated,
|
||
|
|
how many recenters, how far floor moved, and final extraction.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import csv
|
||
|
|
import glob
|
||
|
|
import os
|
||
|
|
from collections import defaultdict
|
||
|
|
|
||
|
|
def analyze_run(path):
|
||
|
|
"""Analyze a single profitable run deeply."""
|
||
|
|
rows = []
|
||
|
|
with open(path) as f:
|
||
|
|
reader = csv.DictReader(f)
|
||
|
|
for row in reader:
|
||
|
|
rows.append(row)
|
||
|
|
|
||
|
|
init_eth = final_eth = 0
|
||
|
|
total_buys = total_sells = 0
|
||
|
|
total_buy_eth = 0
|
||
|
|
total_sell_krk = 0
|
||
|
|
recenters = 0
|
||
|
|
stakes = unstakes = 0
|
||
|
|
|
||
|
|
floor_ticks = []
|
||
|
|
tick_history = []
|
||
|
|
|
||
|
|
# Track phases: accumulation vs extraction
|
||
|
|
phase_transitions = []
|
||
|
|
net_eth_spent = 0 # cumulative ETH spent buying
|
||
|
|
|
||
|
|
for row in rows:
|
||
|
|
action = row['action']
|
||
|
|
amount = int(row.get('amount', 0))
|
||
|
|
floor_lower = int(row.get('floor_lower', 0))
|
||
|
|
tick = int(row.get('tick', 0))
|
||
|
|
eth_bal = int(row.get('eth_balance', 0))
|
||
|
|
krk_bal = int(row.get('kraiken_balance', 0))
|
||
|
|
|
||
|
|
if action == 'INIT':
|
||
|
|
init_eth = eth_bal
|
||
|
|
init_tick = tick
|
||
|
|
elif action == 'FINAL':
|
||
|
|
final_eth = eth_bal
|
||
|
|
final_krk = krk_bal
|
||
|
|
elif action == 'BUY':
|
||
|
|
total_buys += 1
|
||
|
|
total_buy_eth += amount
|
||
|
|
elif action == 'SELL':
|
||
|
|
total_sells += 1
|
||
|
|
total_sell_krk += amount
|
||
|
|
elif action == 'RECENTER':
|
||
|
|
recenters += 1
|
||
|
|
floor_ticks.append(floor_lower)
|
||
|
|
elif action == 'STAKE':
|
||
|
|
stakes += 1
|
||
|
|
elif action == 'UNSTAKE':
|
||
|
|
unstakes += 1
|
||
|
|
|
||
|
|
if floor_lower != 0:
|
||
|
|
tick_history.append((action, tick, floor_lower, eth_bal / 1e18, krk_bal / 1e18))
|
||
|
|
|
||
|
|
pnl = (final_eth - init_eth) / 1e18
|
||
|
|
|
||
|
|
# Floor movement analysis
|
||
|
|
floor_range = 0
|
||
|
|
floor_direction = 0
|
||
|
|
if len(floor_ticks) >= 2:
|
||
|
|
floor_range = max(floor_ticks) - min(floor_ticks)
|
||
|
|
floor_direction = floor_ticks[-1] - floor_ticks[0]
|
||
|
|
|
||
|
|
# Find the key inflection: when does trader switch from accumulation to extraction?
|
||
|
|
# Track ETH balance trajectory
|
||
|
|
eth_trajectory = []
|
||
|
|
for row in rows:
|
||
|
|
if row['action'] in ('BUY', 'SELL', 'RECENTER', 'INIT', 'FINAL'):
|
||
|
|
eth_trajectory.append((row['action'], int(row.get('eth_balance', 0)) / 1e18))
|
||
|
|
|
||
|
|
# Find min ETH point (maximum investment before extraction)
|
||
|
|
min_eth = min(e for _, e in eth_trajectory)
|
||
|
|
min_eth_idx = next(i for i, (_, e) in enumerate(eth_trajectory) if e == min_eth)
|
||
|
|
|
||
|
|
# Phases: before min_eth = accumulation, after = mixed/extraction
|
||
|
|
accum_buys = sum(1 for a, _ in eth_trajectory[:min_eth_idx] if a == 'BUY')
|
||
|
|
accum_sells = sum(1 for a, _ in eth_trajectory[:min_eth_idx] if a == 'SELL')
|
||
|
|
accum_recenters = sum(1 for a, _ in eth_trajectory[:min_eth_idx] if a == 'RECENTER')
|
||
|
|
|
||
|
|
return {
|
||
|
|
'file': os.path.basename(path),
|
||
|
|
'pnl': pnl,
|
||
|
|
'init_eth': init_eth / 1e18,
|
||
|
|
'final_eth': final_eth / 1e18,
|
||
|
|
'total_buys': total_buys,
|
||
|
|
'total_sells': total_sells,
|
||
|
|
'total_buy_eth': total_buy_eth / 1e18,
|
||
|
|
'recenters': recenters,
|
||
|
|
'stakes': stakes,
|
||
|
|
'unstakes': unstakes,
|
||
|
|
'floor_range': floor_range,
|
||
|
|
'floor_direction': floor_direction,
|
||
|
|
'min_eth': min_eth,
|
||
|
|
'max_investment': eth_trajectory[0][1] - min_eth,
|
||
|
|
'accum_buys': accum_buys,
|
||
|
|
'accum_recenters': accum_recenters,
|
||
|
|
}
|
||
|
|
|
||
|
|
def main():
|
||
|
|
csvs = sorted(glob.glob('analysis/fuzz-b*.csv'))
|
||
|
|
|
||
|
|
results = []
|
||
|
|
for path in csvs:
|
||
|
|
try:
|
||
|
|
r = analyze_run(path)
|
||
|
|
if r['pnl'] > 0:
|
||
|
|
results.append(r)
|
||
|
|
except Exception as e:
|
||
|
|
pass
|
||
|
|
|
||
|
|
print(f"Analyzed {len(results)} profitable runs\n")
|
||
|
|
|
||
|
|
# Group by batch
|
||
|
|
batches = defaultdict(list)
|
||
|
|
for r in results:
|
||
|
|
batch = r['file'].split('-')[1]
|
||
|
|
batches[batch].append(r)
|
||
|
|
|
||
|
|
# Overall statistics
|
||
|
|
print("=" * 90)
|
||
|
|
print("DISTILLED ATTACK PATTERNS")
|
||
|
|
print("=" * 90)
|
||
|
|
|
||
|
|
print(f"\n{'Metric':<30s} {'Min':>10s} {'Avg':>10s} {'Max':>10s} {'Median':>10s}")
|
||
|
|
print("-" * 70)
|
||
|
|
|
||
|
|
for metric in ['pnl', 'recenters', 'floor_range', 'total_buys', 'total_sells', 'total_buy_eth', 'max_investment']:
|
||
|
|
vals = sorted([r[metric] for r in results])
|
||
|
|
avg = sum(vals) / len(vals)
|
||
|
|
med = vals[len(vals)//2]
|
||
|
|
print(f"{metric:<30s} {min(vals):>10.1f} {avg:>10.1f} {max(vals):>10.1f} {med:>10.1f}")
|
||
|
|
|
||
|
|
# The essential pattern
|
||
|
|
print("\n" + "=" * 90)
|
||
|
|
print("THE ESSENTIAL ATTACK PATTERN")
|
||
|
|
print("=" * 90)
|
||
|
|
|
||
|
|
# Cluster by PnL
|
||
|
|
high_profit = [r for r in results if r['pnl'] > 100]
|
||
|
|
low_profit = [r for r in results if 0 < r['pnl'] <= 100]
|
||
|
|
|
||
|
|
for label, group in [("HIGH PROFIT (>100 ETH)", high_profit), ("LOW PROFIT (1-100 ETH)", low_profit)]:
|
||
|
|
if not group:
|
||
|
|
continue
|
||
|
|
print(f"\n### {label}: {len(group)} runs")
|
||
|
|
avg = lambda key: sum(r[key] for r in group) / len(group)
|
||
|
|
print(f" Avg PnL: {avg('pnl'):+.1f} ETH")
|
||
|
|
print(f" Avg recenters: {avg('recenters'):.0f}")
|
||
|
|
print(f" Avg floor range: {avg('floor_range'):.0f} ticks")
|
||
|
|
print(f" Avg total buys: {avg('total_buys'):.0f} ({avg('total_buy_eth'):.0f} ETH)")
|
||
|
|
print(f" Avg total sells: {avg('total_sells'):.0f}")
|
||
|
|
print(f" Avg max investment:{avg('max_investment'):.0f} ETH")
|
||
|
|
print(f" Avg stakes: {avg('stakes'):.0f}")
|
||
|
|
|
||
|
|
# Per-batch summary
|
||
|
|
print("\n" + "=" * 90)
|
||
|
|
print("PER-BATCH ATTACK RATES")
|
||
|
|
print("=" * 90)
|
||
|
|
|
||
|
|
batch_info = {
|
||
|
|
'b2': 'buy-heavy CI=0 AS=10% AW=20',
|
||
|
|
'b3': 'sell-heavy CI=0 AS=10% AW=20',
|
||
|
|
'b4': 'wider AS=50% AW=50',
|
||
|
|
'b5': '5000 trades',
|
||
|
|
'b6': 'CI=10% AS=10% AW=20',
|
||
|
|
'b7': 'AS=30% AW=40 buybias=60',
|
||
|
|
'b8': 'AS=100% AW=80',
|
||
|
|
'b9': 'low DD=10%',
|
||
|
|
'b10': 'high DD=100%',
|
||
|
|
'b11': '3000 trades',
|
||
|
|
'b12': 'buy 90%',
|
||
|
|
'b13': 'CI=20%',
|
||
|
|
'b14': 'max AW=100',
|
||
|
|
'b15': 'baseline repeat',
|
||
|
|
'b16': 'AS=20% AW=30',
|
||
|
|
'b17': 'CI=5%',
|
||
|
|
'b18': '4000 trades',
|
||
|
|
'b19': '3000 buybias=65',
|
||
|
|
'b20': 'baseline big',
|
||
|
|
}
|
||
|
|
|
||
|
|
print(f"\n{'Batch':<8s} {'Params':<35s} {'Prof':>5s} {'Avg PnL':>10s} {'Avg Rec':>8s} {'Avg Floor':>10s}")
|
||
|
|
print("-" * 80)
|
||
|
|
|
||
|
|
for batch_id in sorted(batches.keys(), key=lambda x: int(x[1:])):
|
||
|
|
runs = batches[batch_id]
|
||
|
|
info = batch_info.get(batch_id, '?')
|
||
|
|
avg_pnl = sum(r['pnl'] for r in runs) / len(runs)
|
||
|
|
avg_rec = sum(r['recenters'] for r in runs) / len(runs)
|
||
|
|
avg_floor = sum(r['floor_range'] for r in runs) / len(runs)
|
||
|
|
print(f"{batch_id:<8s} {info:<35s} {len(runs):>5d} {avg_pnl:>+10.1f} {avg_rec:>8.0f} {avg_floor:>10.0f}")
|
||
|
|
|
||
|
|
# Distilled archetype
|
||
|
|
print("\n" + "=" * 90)
|
||
|
|
print("DISTILLED ARCHETYPE (what the attacker actually does)")
|
||
|
|
print("=" * 90)
|
||
|
|
avg_all = lambda key: sum(r[key] for r in results) / len(results)
|
||
|
|
print(f"""
|
||
|
|
1. ACCUMULATE: Buy ~{avg_all('total_buy_eth'):.0f} ETH worth of KRK over ~{avg_all('total_buys'):.0f} trades
|
||
|
|
(This crashes the price and triggers recenters)
|
||
|
|
|
||
|
|
2. RECENTERS: ~{avg_all('recenters'):.0f} recenters fire during the sequence
|
||
|
|
Floor moves ~{avg_all('floor_range'):.0f} ticks closer to current price
|
||
|
|
(Each recenter repositions floor, packing ETH into reachable range)
|
||
|
|
|
||
|
|
3. EXTRACT: Sell all KRK (~{avg_all('total_sells'):.0f} sell trades)
|
||
|
|
Final sell drains floor's concentrated ETH reserve
|
||
|
|
|
||
|
|
NET: Trader invests ~{avg_all('max_investment'):.0f} ETH at peak, extracts {avg_all('pnl'):+.0f} ETH profit
|
||
|
|
|
||
|
|
STAKING: {avg_all('stakes'):.0f} stakes / {avg_all('unstakes'):.0f} unstakes — NO CORRELATION with profit
|
||
|
|
""")
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|