{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 05 — Augmented Bonding Curve & Market Analysis\n",
    "\n",
    "The core questions:\n",
    "1. How did the ABC price track vs secondary market (Honeyswap/Velodrome)?\n",
    "2. Was the buy/sell ratio sustainable? (Spoiler: 2,217 sells vs 398 buys)\n",
    "3. How much tribute revenue did the ABC generate?\n",
    "4. How concentrated was token ownership?\n",
    "5. Did price discovery happen on the ABC or the DEX?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:40.211679Z",
     "iopub.status.busy": "2026-04-03T21:30:40.211552Z",
     "iopub.status.idle": "2026-04-03T21:30:41.774877Z",
     "shell.execute_reply": "2026-04-03T21:30:41.773500Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ABC trades (detailed): 2615 rows, 2022-01-25 to 2023-12-18\n",
      "ABC trades (all):      32000 rows\n",
      "DEX trades:            26107 rows, 2022-01-15 to 2026-03-31\n",
      "  Projects: <StringArray>\n",
      "['honeyswap', 'velodrome']\n",
      "Length: 2, dtype: str\n",
      "ABC action counts: {'Sell': 2217, 'Buy': 398}\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.dates as mdates\n",
    "import seaborn as sns\n",
    "\n",
    "sns.set_theme(style='whitegrid', palette='deep')\n",
    "plt.rcParams['figure.figsize'] = (14, 6)\n",
    "plt.rcParams['figure.dpi'] = 100\n",
    "\n",
    "DATA = '../data/onchain'\n",
    "\n",
    "# Load datasets\n",
    "abc_trades = pd.read_csv(f'{DATA}/dune_q2994918.csv')  # ABC with reserve/supply\n",
    "abc_all = pd.read_csv(f'{DATA}/dune_q3743980.csv')     # All ABC trades\n",
    "dex_all = pd.read_csv(f'{DATA}/dune_q3743672.csv')     # All DEX trades\n",
    "dex_buys = pd.read_csv(f'{DATA}/dune_q3743698.csv')    # DEX buys\n",
    "dex_sells = pd.read_csv(f'{DATA}/dune_q3743704.csv')   # DEX sells\n",
    "tributes_a = pd.read_csv(f'{DATA}/dune_q3744004.csv')  # Tributes monthly\n",
    "tributes_b = pd.read_csv(f'{DATA}/dune_q3744008.csv')  # Tributes monthly alt\n",
    "holders = pd.read_csv(f'{DATA}/dune_q3743746.csv')     # Top holders\n",
    "holder_hist = pd.read_csv(f'{DATA}/dune_q3743731.csv') # Holder count over time\n",
    "supply = pd.read_csv(f'{DATA}/dune_q3743718.csv')      # Holders & supply\n",
    "trade_summary = pd.read_csv(f'{DATA}/dune_q3743710.csv')  # Trade action summary\n",
    "\n",
    "# Parse dates\n",
    "abc_trades['date'] = pd.to_datetime(abc_trades['block_time'])\n",
    "abc_all['date'] = pd.to_datetime(abc_all['block_time'])\n",
    "dex_all['date'] = pd.to_datetime(dex_all['block_time'])\n",
    "\n",
    "print(f'ABC trades (detailed): {len(abc_trades)} rows, {abc_trades[\"date\"].min().date()} to {abc_trades[\"date\"].max().date()}')\n",
    "print(f'ABC trades (all):      {len(abc_all)} rows')\n",
    "print(f'DEX trades:            {len(dex_all)} rows, {dex_all[\"date\"].min().date()} to {dex_all[\"date\"].max().date()}')\n",
    "print(f'  Projects: {dex_all[\"project\"].unique()}')\n",
    "print(f'ABC action counts: {abc_trades[\"action\"].value_counts().to_dict()}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. ABC Price History & Reserve Ratio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:41.813446Z",
     "iopub.status.busy": "2026-04-03T21:30:41.813169Z",
     "iopub.status.idle": "2026-04-03T21:30:42.910245Z",
     "shell.execute_reply": "2026-04-03T21:30:42.908932Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 165100 characters]",
      "text/plain": [
       "<Figure size 1600x1400 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "ABC Price Stats:\n",
      "  Peak: 2.9444 xDAI\n",
      "  Trough: 0.3613 xDAI\n",
      "  Drawdown: -87.7%\n",
      "\n",
      "Reserve:\n",
      "  Peak: 1,119,156 xDAI\n",
      "  Final: 111,623 xDAI\n",
      "\n",
      "Supply:\n",
      "  Peak: 2,451,021 TEC\n",
      "  Final: 1,546,631 TEC\n"
     ]
    }
   ],
   "source": [
    "abc_sorted = abc_trades.sort_values('date').copy()\n",
    "\n",
    "fig, axes = plt.subplots(3, 1, figsize=(16, 14), sharex=True)\n",
    "\n",
    "# Price over time colored by action\n",
    "ax = axes[0]\n",
    "buys = abc_sorted[abc_sorted['action'] == 'Buy']\n",
    "sells = abc_sorted[abc_sorted['action'] == 'Sell']\n",
    "ax.scatter(buys['date'], buys['price_per_token'], c='#2ecc71', s=15, alpha=0.5, label=f'Buy ({len(buys)})')\n",
    "ax.scatter(sells['date'], sells['price_per_token'], c='#e74c3c', s=15, alpha=0.5, label=f'Sell ({len(sells)})')\n",
    "ax.set_ylabel('Price (xDAI per TEC)')\n",
    "ax.set_title('ABC Price Over Time')\n",
    "ax.legend()\n",
    "\n",
    "# Reserve balance\n",
    "ax = axes[1]\n",
    "ax.plot(abc_sorted['date'], abc_sorted['reserve_balance'], color='#3498db', linewidth=1)\n",
    "ax.fill_between(abc_sorted['date'], abc_sorted['reserve_balance'], alpha=0.2, color='#3498db')\n",
    "ax.set_ylabel('Reserve Balance (xDAI)')\n",
    "ax.set_title('ABC Reserve Pool Over Time')\n",
    "\n",
    "# Supply\n",
    "ax = axes[2]\n",
    "ax.plot(abc_sorted['date'], abc_sorted['cumulative_supply'], color='teal', linewidth=1)\n",
    "ax.fill_between(abc_sorted['date'], abc_sorted['cumulative_supply'], alpha=0.2, color='teal')\n",
    "ax.set_ylabel('Token Supply')\n",
    "ax.set_title('TEC Circulating Supply')\n",
    "\n",
    "for ax in axes:\n",
    "    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "    ax.xaxis.set_major_locator(mdates.MonthLocator(interval=3))\n",
    "\n",
    "plt.setp(axes[-1].xaxis.get_majorticklabels(), rotation=45)\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/abc_price_reserve.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "print(f'\\nABC Price Stats:')\n",
    "print(f'  Peak: {abc_sorted[\"price_per_token\"].max():.4f} xDAI')\n",
    "print(f'  Trough: {abc_sorted[\"price_per_token\"].min():.4f} xDAI')\n",
    "print(f'  Drawdown: {(abc_sorted[\"price_per_token\"].min() / abc_sorted[\"price_per_token\"].max() - 1):.1%}')\n",
    "print(f'\\nReserve:')\n",
    "print(f'  Peak: {abc_sorted[\"reserve_balance\"].max():,.0f} xDAI')\n",
    "print(f'  Final: {abc_sorted[\"reserve_balance\"].iloc[-1]:,.0f} xDAI')\n",
    "print(f'\\nSupply:')\n",
    "print(f'  Peak: {abc_sorted[\"cumulative_supply\"].max():,.0f} TEC')\n",
    "print(f'  Final: {abc_sorted[\"cumulative_supply\"].iloc[-1]:,.0f} TEC')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Buy/Sell Asymmetry — The Structural Pressure"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:42.913669Z",
     "iopub.status.busy": "2026-04-03T21:30:42.913176Z",
     "iopub.status.idle": "2026-04-03T21:30:44.341534Z",
     "shell.execute_reply": "2026-04-03T21:30:44.340421Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_56299/821195215.py:3: UserWarning: Converting to PeriodArray/Index representation will drop timezone information.\n",
      "  abc_monthly['month'] = abc_monthly['date'].dt.to_period('M').dt.to_timestamp()\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 150312 characters]",
      "text/plain": [
       "<Figure size 1800x1000 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Buy/Sell Analysis:\n",
      "  Total buys: 398 txns, 865,057 xDAI\n",
      "  Total sells: 2217 txns, 1,031,596 xDAI\n",
      "  Sell:Buy ratio (count): 5.6:1\n",
      "  Sell:Buy ratio (volume): 1.2:1\n",
      "  Net outflow: 166,538 xDAI\n"
     ]
    }
   ],
   "source": [
    "# Monthly buy vs sell volume\n",
    "abc_monthly = abc_sorted.copy()\n",
    "abc_monthly['month'] = abc_monthly['date'].dt.to_period('M').dt.to_timestamp()\n",
    "\n",
    "monthly_agg = abc_monthly.groupby(['month', 'action']).agg(\n",
    "    count=('Amount', 'count'),\n",
    "    total_amount=('Amount', 'sum'),\n",
    "    total_tokens=('noToken', 'sum'),\n",
    "    avg_price=('price_per_token', 'mean'),\n",
    ").reset_index()\n",
    "\n",
    "fig, axes = plt.subplots(2, 2, figsize=(18, 10))\n",
    "\n",
    "# Transaction count by month\n",
    "ax = axes[0, 0]\n",
    "for action, color in [('Buy', '#2ecc71'), ('Sell', '#e74c3c')]:\n",
    "    subset = monthly_agg[monthly_agg['action'] == action]\n",
    "    ax.bar(subset['month'] + pd.Timedelta(days=8 if action == 'Sell' else -8),\n",
    "           subset['count'], width=15, color=color, alpha=0.7, label=action)\n",
    "ax.set_ylabel('Transaction Count')\n",
    "ax.set_title('Monthly ABC Transaction Count')\n",
    "ax.legend()\n",
    "\n",
    "# Volume by month (xDAI amount)\n",
    "ax = axes[0, 1]\n",
    "for action, color in [('Buy', '#2ecc71'), ('Sell', '#e74c3c')]:\n",
    "    subset = monthly_agg[monthly_agg['action'] == action]\n",
    "    ax.bar(subset['month'] + pd.Timedelta(days=8 if action == 'Sell' else -8),\n",
    "           subset['total_amount'], width=15, color=color, alpha=0.7, label=action)\n",
    "ax.set_ylabel('Volume (xDAI)')\n",
    "ax.set_title('Monthly ABC Volume')\n",
    "ax.legend()\n",
    "\n",
    "# Buy/sell ratio over time\n",
    "ax = axes[1, 0]\n",
    "buy_monthly = monthly_agg[monthly_agg['action'] == 'Buy'].set_index('month')['count']\n",
    "sell_monthly = monthly_agg[monthly_agg['action'] == 'Sell'].set_index('month')['count']\n",
    "ratio = (buy_monthly / sell_monthly).dropna()\n",
    "colors = ['#2ecc71' if r >= 1 else '#e74c3c' for r in ratio]\n",
    "ax.bar(ratio.index, ratio.values, width=20, color=colors, alpha=0.7)\n",
    "ax.axhline(y=1, color='black', linestyle='--', alpha=0.5, label='Parity')\n",
    "ax.set_ylabel('Buy/Sell Ratio')\n",
    "ax.set_title('Monthly Buy/Sell Ratio (<1 = net selling pressure)')\n",
    "ax.legend()\n",
    "\n",
    "# Cumulative net flow\n",
    "ax = axes[1, 1]\n",
    "abc_sorted_copy = abc_sorted.copy()\n",
    "abc_sorted_copy['signed_amount'] = abc_sorted_copy.apply(\n",
    "    lambda r: r['Amount'] if r['action'] == 'Buy' else -r['Amount'], axis=1\n",
    ")\n",
    "abc_sorted_copy['cum_flow'] = abc_sorted_copy['signed_amount'].cumsum()\n",
    "ax.plot(abc_sorted_copy['date'], abc_sorted_copy['cum_flow'], color='steelblue', linewidth=1.5)\n",
    "ax.fill_between(abc_sorted_copy['date'], abc_sorted_copy['cum_flow'],\n",
    "                where=abc_sorted_copy['cum_flow'] >= 0, color='#2ecc71', alpha=0.2)\n",
    "ax.fill_between(abc_sorted_copy['date'], abc_sorted_copy['cum_flow'],\n",
    "                where=abc_sorted_copy['cum_flow'] < 0, color='#e74c3c', alpha=0.2)\n",
    "ax.axhline(y=0, color='black', linewidth=0.5)\n",
    "ax.set_ylabel('Cumulative Net Flow (xDAI)')\n",
    "ax.set_title('Cumulative Buy - Sell Flow (negative = capital leaving)')\n",
    "\n",
    "for row in axes:\n",
    "    for ax in row:\n",
    "        ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/abc_buysell.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "total_buy = abc_sorted[abc_sorted['action'] == 'Buy']['Amount'].sum()\n",
    "total_sell = abc_sorted[abc_sorted['action'] == 'Sell']['Amount'].sum()\n",
    "print(f'\\nBuy/Sell Analysis:')\n",
    "print(f'  Total buys: {len(buys)} txns, {total_buy:,.0f} xDAI')\n",
    "print(f'  Total sells: {len(sells)} txns, {total_sell:,.0f} xDAI')\n",
    "print(f'  Sell:Buy ratio (count): {len(sells)/len(buys):.1f}:1')\n",
    "print(f'  Sell:Buy ratio (volume): {total_sell/total_buy:.1f}:1')\n",
    "print(f'  Net outflow: {total_sell - total_buy:,.0f} xDAI')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. ABC Tribute Revenue"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:44.343917Z",
     "iopub.status.busy": "2026-04-03T21:30:44.343759Z",
     "iopub.status.idle": "2026-04-03T21:30:45.079587Z",
     "shell.execute_reply": "2026-04-03T21:30:45.078376Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 81872 characters]",
      "text/plain": [
       "<Figure size 1600x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Tribute Revenue:\n",
      "  Buy tribute: 210,490.63 xDAI\n",
      "  Sell tribute: 119,413.19 xDAI\n",
      "  Total: 329,903.82 xDAI\n"
     ]
    }
   ],
   "source": [
    "# Tribute from individual trades\n",
    "abc_sorted['tribute_val'] = pd.to_numeric(abc_sorted['tribute'], errors='coerce')\n",
    "tribute_by_action = abc_sorted.groupby('action')['tribute_val'].sum()\n",
    "\n",
    "fig, axes = plt.subplots(1, 2, figsize=(16, 6))\n",
    "\n",
    "# Cumulative tribute\n",
    "ax = axes[0]\n",
    "for action, color in [('Buy', '#2ecc71'), ('Sell', '#e74c3c')]:\n",
    "    subset = abc_sorted[abc_sorted['action'] == action].copy()\n",
    "    subset['cum_tribute'] = subset['tribute_val'].cumsum()\n",
    "    ax.plot(subset['date'], subset['cum_tribute'], color=color, linewidth=1.5, label=f'{action} tribute')\n",
    "ax.set_ylabel('Cumulative Tribute (xDAI)')\n",
    "ax.set_title('ABC Tribute Revenue Over Time')\n",
    "ax.legend()\n",
    "\n",
    "# Monthly tribute from aggregated data\n",
    "ax = axes[1]\n",
    "# Use the detailed tribute tables\n",
    "for df, label in [(tributes_a, 'Post-migration'), (tributes_b, 'Alt')]:\n",
    "    df['date'] = pd.to_datetime(df['date'])\n",
    "\n",
    "t_pivot = tributes_a.pivot_table(index='date', columns='action', values='Tribute_Distribution', aggfunc='sum').fillna(0)\n",
    "if 'Buy' in t_pivot.columns:\n",
    "    ax.bar(t_pivot.index, t_pivot.get('Buy', 0), width=20, color='#2ecc71', alpha=0.7, label='Buy tribute')\n",
    "if 'Sell' in t_pivot.columns:\n",
    "    ax.bar(t_pivot.index, t_pivot.get('Sell', 0), width=20, color='#e74c3c', alpha=0.7,\n",
    "           bottom=t_pivot.get('Buy', 0), label='Sell tribute')\n",
    "ax.set_ylabel('Tribute (xDAI)')\n",
    "ax.set_title('Monthly ABC Tribute Revenue')\n",
    "ax.legend()\n",
    "\n",
    "for ax in axes:\n",
    "    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/abc_tributes.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "print(f'\\nTribute Revenue:')\n",
    "for action in ['Buy', 'Sell']:\n",
    "    t = tribute_by_action.get(action, 0)\n",
    "    print(f'  {action} tribute: {t:,.2f} xDAI')\n",
    "print(f'  Total: {tribute_by_action.sum():,.2f} xDAI')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Primary (ABC) vs Secondary (DEX) Market\n",
    "\n",
    "Where did price discovery happen? Did the ABC price diverge from the DEX price?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:45.082541Z",
     "iopub.status.busy": "2026-04-03T21:30:45.082332Z",
     "iopub.status.idle": "2026-04-03T21:30:46.173321Z",
     "shell.execute_reply": "2026-04-03T21:30:46.171923Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Price Spread Stats:\n",
      "  Mean spread: -9.6%\n",
      "  Median spread: -11.6%\n",
      "  Max DEX premium: 43.6%\n",
      "  Max DEX discount: -40.7%\n",
      "  Days with DEX discount: 433/501\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 326004 characters]",
      "text/plain": [
       "<Figure size 1600x1000 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Extract price from DEX trades\n",
    "# DEX buys and sells have price_per_token column\n",
    "dex_with_price = pd.concat([\n",
    "    dex_buys[['block_time', 'project', 'action', 'price_per_token', 'Amount']].copy(),\n",
    "    dex_sells[['block_time', 'project', 'action', 'price_per_token', 'Amount']].copy(),\n",
    "])\n",
    "dex_with_price['date'] = pd.to_datetime(dex_with_price['block_time'])\n",
    "dex_with_price['price_per_token'] = pd.to_numeric(dex_with_price['price_per_token'], errors='coerce')\n",
    "dex_with_price = dex_with_price.dropna(subset=['price_per_token'])\n",
    "dex_with_price = dex_with_price[dex_with_price['price_per_token'] > 0]\n",
    "\n",
    "# Daily average price from each venue\n",
    "abc_daily_price = abc_sorted.set_index('date')['price_per_token'].resample('D').mean().dropna()\n",
    "dex_daily_price = dex_with_price.set_index('date')['price_per_token'].resample('D').mean().dropna()\n",
    "\n",
    "fig, axes = plt.subplots(2, 1, figsize=(16, 10))\n",
    "\n",
    "# Price comparison\n",
    "ax = axes[0]\n",
    "ax.plot(abc_daily_price.index, abc_daily_price.values, label='ABC Price', color='steelblue', linewidth=1, alpha=0.8)\n",
    "ax.plot(dex_daily_price.index, dex_daily_price.values, label='DEX Price', color='coral', linewidth=1, alpha=0.8)\n",
    "ax.set_ylabel('Price (xDAI per TEC)')\n",
    "ax.set_title('ABC vs DEX Price — Where did price discovery happen?')\n",
    "ax.legend()\n",
    "\n",
    "# Price spread/premium\n",
    "ax = axes[1]\n",
    "aligned_prices = pd.DataFrame({\n",
    "    'abc': abc_daily_price,\n",
    "    'dex': dex_daily_price\n",
    "}).dropna()\n",
    "if len(aligned_prices) > 0:\n",
    "    spread = (aligned_prices['dex'] - aligned_prices['abc']) / aligned_prices['abc'] * 100\n",
    "    ax.fill_between(spread.index, spread, where=spread >= 0, color='#2ecc71', alpha=0.3, label='DEX premium')\n",
    "    ax.fill_between(spread.index, spread, where=spread < 0, color='#e74c3c', alpha=0.3, label='DEX discount')\n",
    "    ax.plot(spread.index, spread, color='black', linewidth=0.5)\n",
    "    ax.axhline(y=0, color='black', linewidth=0.5)\n",
    "    ax.set_ylabel('DEX Premium/Discount (%)')\n",
    "    ax.set_title('DEX Price Relative to ABC (+ = DEX more expensive)')\n",
    "    ax.legend()\n",
    "    \n",
    "    print(f'\\nPrice Spread Stats:')\n",
    "    print(f'  Mean spread: {spread.mean():.1f}%')\n",
    "    print(f'  Median spread: {spread.median():.1f}%')\n",
    "    print(f'  Max DEX premium: {spread.max():.1f}%')\n",
    "    print(f'  Max DEX discount: {spread.min():.1f}%')\n",
    "    print(f'  Days with DEX discount: {(spread < 0).sum()}/{len(spread)}')\n",
    "else:\n",
    "    ax.text(0.5, 0.5, 'Insufficient overlapping price data', transform=ax.transAxes, ha='center')\n",
    "\n",
    "for ax in axes:\n",
    "    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/abc_vs_dex.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Token Holder Concentration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:46.176714Z",
     "iopub.status.busy": "2026-04-03T21:30:46.176550Z",
     "iopub.status.idle": "2026-04-03T21:30:46.785300Z",
     "shell.execute_reply": "2026-04-03T21:30:46.783302Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 108264 characters]",
      "text/plain": [
       "<Figure size 1600x700 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Holder Concentration:\n",
      "  Total holders: 723\n",
      "  Top 1 (gideonro.eth): 13.2% of supply\n",
      "  Top 10: 50.7% of supply\n",
      "  50% of supply held by top 10 holders\n",
      "  80% of supply held by top 43 holders\n"
     ]
    }
   ],
   "source": [
    "# Clean holders data (remove HTML from address column)\n",
    "holders_clean = holders.copy()\n",
    "holders_clean['addr'] = holders_clean['address_raw']\n",
    "\n",
    "fig, axes = plt.subplots(1, 2, figsize=(16, 7))\n",
    "\n",
    "# Top 20 holders\n",
    "ax = axes[0]\n",
    "top20 = holders_clean.head(20)\n",
    "labels = top20.apply(\n",
    "    lambda r: r['ens_name'] if pd.notna(r['ens_name']) and len(str(r['ens_name'])) < 20\n",
    "    else f\"{str(r['addr'])[:8]}...\", axis=1\n",
    ")\n",
    "ax.barh(range(len(top20)), top20['balance'], color='steelblue')\n",
    "ax.set_yticks(range(len(top20)))\n",
    "ax.set_yticklabels(labels, fontsize=8)\n",
    "ax.invert_yaxis()\n",
    "ax.set_xlabel('TEC Balance')\n",
    "ax.set_title(f'Top 20 Token Holders')\n",
    "\n",
    "# Cumulative concentration\n",
    "ax = axes[1]\n",
    "ax.plot(holders_clean['rank_number'], holders_clean['cumulative_perc'] * 100,\n",
    "        color='coral', linewidth=2)\n",
    "ax.axhline(y=50, color='gray', linestyle='--', alpha=0.5)\n",
    "ax.axhline(y=80, color='gray', linestyle='--', alpha=0.5)\n",
    "# Find the rank at 50% and 80%\n",
    "rank_50 = holders_clean[holders_clean['cumulative_perc'] >= 0.5].iloc[0]['rank_number']\n",
    "rank_80 = holders_clean[holders_clean['cumulative_perc'] >= 0.8].iloc[0]['rank_number']\n",
    "ax.axvline(x=rank_50, color='gray', linestyle=':', alpha=0.5)\n",
    "ax.axvline(x=rank_80, color='gray', linestyle=':', alpha=0.5)\n",
    "ax.text(rank_50 + 5, 48, f'50% at rank {int(rank_50)}', fontsize=9)\n",
    "ax.text(rank_80 + 5, 78, f'80% at rank {int(rank_80)}', fontsize=9)\n",
    "ax.set_xlabel('Holder Rank')\n",
    "ax.set_ylabel('Cumulative % of Supply')\n",
    "ax.set_title('Token Concentration Curve')\n",
    "ax.set_xlim(0, min(200, len(holders_clean)))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/holder_concentration.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "top1 = holders_clean.iloc[0]\n",
    "top10_pct = holders_clean.head(10)['cumulative_perc'].iloc[-1]\n",
    "print(f'\\nHolder Concentration:')\n",
    "print(f'  Total holders: {len(holders_clean)}')\n",
    "print(f'  Top 1 ({top1[\"ens_name\"]}): {top1[\"perc\"]:.1%} of supply')\n",
    "print(f'  Top 10: {top10_pct:.1%} of supply')\n",
    "print(f'  50% of supply held by top {int(rank_50)} holders')\n",
    "print(f'  80% of supply held by top {int(rank_80)} holders')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Holder Growth Over Time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:46.788680Z",
     "iopub.status.busy": "2026-04-03T21:30:46.788394Z",
     "iopub.status.idle": "2026-04-03T21:30:49.317452Z",
     "shell.execute_reply": "2026-04-03T21:30:49.314109Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 103216 characters]",
      "text/plain": [
       "<Figure size 1600x1000 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Holder dynamics:\n",
      "  gnosis: peak 737 holders, final 732, net change +728\n",
      "  optimism: peak 814 holders, final 723, net change +2\n"
     ]
    }
   ],
   "source": [
    "holder_hist['date'] = pd.to_datetime(holder_hist['Date'])\n",
    "\n",
    "fig, axes = plt.subplots(2, 1, figsize=(16, 10))\n",
    "\n",
    "# Total holders over time\n",
    "ax = axes[0]\n",
    "for chain in holder_hist['chain'].unique():\n",
    "    subset = holder_hist[holder_hist['chain'] == chain].sort_values('date')\n",
    "    ax.plot(subset['date'], subset['Holders'], label=chain, linewidth=1.5)\n",
    "ax.set_ylabel('Total Holders')\n",
    "ax.set_title('TEC Token Holders Over Time')\n",
    "ax.legend()\n",
    "\n",
    "# Net holder changes\n",
    "ax = axes[1]\n",
    "for chain in holder_hist['chain'].unique():\n",
    "    subset = holder_hist[holder_hist['chain'] == chain].sort_values('date')\n",
    "    colors = ['#2ecc71' if c >= 0 else '#e74c3c' for c in subset['Changes']]\n",
    "    ax.bar(subset['date'], subset['Changes'], color=colors, width=1, alpha=0.6, label=chain)\n",
    "ax.axhline(y=0, color='black', linewidth=0.5)\n",
    "ax.set_ylabel('Net Holder Change')\n",
    "ax.set_title('Daily Net Holder Changes (+ = new holders, - = leaving)')\n",
    "ax.legend()\n",
    "\n",
    "for ax in axes:\n",
    "    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/holder_growth.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "net_change = holder_hist.groupby('chain')['Changes'].sum()\n",
    "print(f'\\nHolder dynamics:')\n",
    "for chain, change in net_change.items():\n",
    "    peak = holder_hist[holder_hist['chain'] == chain]['Holders'].max()\n",
    "    final = holder_hist[holder_hist['chain'] == chain].sort_values('date')['Holders'].iloc[-1]\n",
    "    print(f'  {chain}: peak {peak} holders, final {final}, net change {change:+d}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. DEX Volume & Liquidity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:49.321795Z",
     "iopub.status.busy": "2026-04-03T21:30:49.321389Z",
     "iopub.status.idle": "2026-04-03T21:30:50.247496Z",
     "shell.execute_reply": "2026-04-03T21:30:50.245619Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_56299/1410140200.py:3: UserWarning: Converting to PeriodArray/Index representation will drop timezone information.\n",
      "  dex_all['month'] = dex_all['date'].dt.to_period('M').dt.to_timestamp()\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 86000 characters]",
      "text/plain": [
       "<Figure size 1600x1000 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "DEX Activity:\n",
      "  Total trades: 26107\n",
      "  honeyswap: 20404 trades\n",
      "  velodrome: 5703 trades\n"
     ]
    }
   ],
   "source": [
    "# DEX trade volume over time\n",
    "dex_all['amount_usd_num'] = pd.to_numeric(dex_all['amount_usd'], errors='coerce')\n",
    "dex_all['month'] = dex_all['date'].dt.to_period('M').dt.to_timestamp()\n",
    "\n",
    "dex_monthly = dex_all.groupby(['month', 'project']).agg(\n",
    "    trade_count=('block_time', 'count'),\n",
    "    volume_usd=('amount_usd_num', 'sum'),\n",
    ").reset_index()\n",
    "\n",
    "fig, axes = plt.subplots(2, 1, figsize=(16, 10))\n",
    "\n",
    "# Trade count\n",
    "ax = axes[0]\n",
    "for project in dex_monthly['project'].unique():\n",
    "    subset = dex_monthly[dex_monthly['project'] == project]\n",
    "    ax.bar(subset['month'], subset['trade_count'], width=20, alpha=0.7, label=project)\n",
    "ax.set_ylabel('Trade Count')\n",
    "ax.set_title('Monthly DEX Trade Count')\n",
    "ax.legend()\n",
    "\n",
    "# USD Volume (where available)\n",
    "ax = axes[1]\n",
    "vol = dex_monthly[dex_monthly['volume_usd'] > 0]\n",
    "if len(vol) > 0:\n",
    "    for project in vol['project'].unique():\n",
    "        subset = vol[vol['project'] == project]\n",
    "        ax.bar(subset['month'], subset['volume_usd'], width=20, alpha=0.7, label=project)\n",
    "    ax.set_ylabel('Volume (USD)')\n",
    "    ax.set_title('Monthly DEX Volume (USD)')\n",
    "    ax.legend()\n",
    "else:\n",
    "    ax.text(0.5, 0.5, 'USD volume data not available for most trades',\n",
    "            transform=ax.transAxes, ha='center', fontsize=12)\n",
    "\n",
    "for ax in axes:\n",
    "    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/dex_volume.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "print(f'\\nDEX Activity:')\n",
    "print(f'  Total trades: {len(dex_all)}')\n",
    "for project in dex_all['project'].unique():\n",
    "    n = len(dex_all[dex_all['project'] == project])\n",
    "    print(f'  {project}: {n} trades')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. The 500K Whale — Largest ABC Participant\n",
    "\n",
    "Wallet `0xf5dcd98a...12f35497` made a single 499,803 xDAI purchase on Jan 31, 2022 — the largest ABC buy in TEC history, representing 46.5% of all ABC buy volume. This section traces their complete entry/exit trajectory and tribute contribution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:50.250976Z",
     "iopub.status.busy": "2026-04-03T21:30:50.250607Z",
     "iopub.status.idle": "2026-04-03T21:30:51.618956Z",
     "shell.execute_reply": "2026-04-03T21:30:51.616521Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 177056 characters]",
      "text/plain": [
       "<Figure size 1800x1200 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "500K WHALE DEEP DIVE (0xf5dcd98a1c99c2c65c62025cb23cfb6f12f35497)\n",
      "=================================================================\n",
      "\n",
      "ENTRY (2 buys on Jan 25, 2022):\n",
      "  Total spent: 499,803 xDAI (incl 109,957 xDAI entry tribute)\n",
      "  TEC minted:  207,906 TEC\n",
      "  Avg price:   2.4040 xDAI/TEC (all-in)\n",
      "  This single purchase was 57.8% of ALL ABC buy volume\n",
      "\n",
      "EXIT (6 sells, May 2022 - Jun 2023):\n",
      "  2022-05-16:     24,939 TEC @ 6.33 xDAI/TEC =    157,906 xDAI (exit tribute: 13,731)\n",
      "  2022-06-06:      7,338 TEC @ 6.81 xDAI/TEC =     50,000 xDAI (exit tribute: 4,945)\n",
      "  2022-11-01:      2,049 TEC @ 6.59 xDAI/TEC =     13,498 xDAI (exit tribute: 1,841)\n",
      "  2023-02-12:      1,628 TEC @ 8.75 xDAI/TEC =     14,249 xDAI (exit tribute: 1,943)\n",
      "  2023-05-01:        850 TEC @ 12.58 xDAI/TEC =     10,693 xDAI (exit tribute: 1,458)\n",
      "  2023-06-09:        358 TEC @ 14.97 xDAI/TEC =      5,366 xDAI (exit tribute: 732)\n",
      "  Total received: 251,713 xDAI from 37,163 TEC sold\n",
      "\n",
      "TRIBUTE CONTRIBUTION:\n",
      "  Entry tribute: 109,957 xDAI\n",
      "  Exit tribute:  24,650 xDAI\n",
      "  Total:         134,606 xDAI to common pool\n",
      "\n",
      "NET POSITION:\n",
      "  Realized P&L:  -248,090 xDAI (-49.6%)\n",
      "  TEC remaining: 170,743 (from ABC) + ~50,000 (from Hatch)\n",
      "  Current holder rank: #36 with 6,203 TEC\n",
      "  Implied disposal: ~214,540 TEC transferred/sold elsewhere\n"
     ]
    }
   ],
   "source": [
    "# Whale analysis: wallet 0xf5dcd98a1c99c2c65c62025cb23cfb6f12f35497\n",
    "import json as _json, datetime as _dt\n",
    "\n",
    "WHALE = '0xf5dcd98a1c99c2c65c62025cb23cfb6f12f35497'\n",
    "\n",
    "# Decode ABC events from cached RPC response\n",
    "with open('/tmp/abc_wallet_events.json') as f:\n",
    "    rpc = _json.load(f)\n",
    "events = rpc['result']\n",
    "\n",
    "BUY_TOPIC = '0x6e4172e8e5f0f61687be128a590196cf25641492f0c8924cdcbae1253e0f8ad1'\n",
    "SELL_TOPIC = '0x1a4333c1f7bf9654d8e7a04772ec0d39d96908d91b0c2aba9ae8225985d0d205'\n",
    "\n",
    "whale_trades = []\n",
    "for evt in events:\n",
    "    data = evt['data'][2:]\n",
    "    chunks = [int(data[i:i+64], 16) / 1e18 for i in range(0, len(data), 64)]\n",
    "    ts = int(evt['blockTimestamp'], 16)\n",
    "    dt = _dt.datetime.fromtimestamp(ts)\n",
    "    block = int(evt['blockNumber'], 16)\n",
    "\n",
    "    if evt['topics'][0] == BUY_TOPIC:\n",
    "        # MakeBuyOrder: [tribute_amt, collateral_to_reserve, tec_minted, tribute_rate]\n",
    "        tribute = chunks[0]\n",
    "        collateral = chunks[1]\n",
    "        tec_minted = chunks[2]\n",
    "        whale_trades.append({\n",
    "            'date': dt, 'block': block, 'action': 'Buy',\n",
    "            'xdai_total': tribute + collateral,\n",
    "            'xdai_net': collateral,\n",
    "            'tec_amount': tec_minted,\n",
    "            'tribute': tribute,\n",
    "            'tribute_rate': chunks[3],\n",
    "            'tx_hash': evt['transactionHash'],\n",
    "        })\n",
    "    elif evt['topics'][0] == SELL_TOPIC:\n",
    "        # MakeSellOrder: [tec_burned, xdai_returned, reserve_after, exit_tribute_rate]\n",
    "        tec_burned = chunks[0]\n",
    "        xdai_returned = chunks[1]\n",
    "        exit_rate = chunks[3]\n",
    "        exit_tribute = xdai_returned / (1 - exit_rate) * exit_rate\n",
    "        whale_trades.append({\n",
    "            'date': dt, 'block': block, 'action': 'Sell',\n",
    "            'xdai_total': xdai_returned,\n",
    "            'xdai_net': xdai_returned,\n",
    "            'tec_amount': tec_burned,\n",
    "            'tribute': exit_tribute,\n",
    "            'tribute_rate': exit_rate,\n",
    "            'tx_hash': evt['transactionHash'],\n",
    "        })\n",
    "\n",
    "whale_df = pd.DataFrame(whale_trades).sort_values('date')\n",
    "\n",
    "# Effective price: for buys = total_xdai / tec; for sells = xdai_returned / tec\n",
    "whale_df['effective_price'] = whale_df['xdai_total'] / whale_df['tec_amount']\n",
    "\n",
    "# Running P&L\n",
    "tec_balance = 0\n",
    "total_invested = 0\n",
    "total_received = 0\n",
    "records = []\n",
    "for _, row in whale_df.iterrows():\n",
    "    if row['action'] == 'Buy':\n",
    "        tec_balance += row['tec_amount']\n",
    "        total_invested += row['xdai_total']\n",
    "    else:\n",
    "        tec_balance -= row['tec_amount']\n",
    "        total_received += row['xdai_net']\n",
    "    records.append({\n",
    "        'date': row['date'], 'action': row['action'],\n",
    "        'tec_balance': tec_balance,\n",
    "        'total_invested': total_invested,\n",
    "        'total_received': total_received,\n",
    "        'pnl': total_received - total_invested,\n",
    "    })\n",
    "pnl_df = pd.DataFrame(records)\n",
    "\n",
    "HATCH_TEC = 50000  # approx from Hatch genesis\n",
    "\n",
    "fig, axes = plt.subplots(2, 2, figsize=(18, 12))\n",
    "\n",
    "# Panel 1: Trade prices vs ABC market\n",
    "ax = axes[0, 0]\n",
    "buys_w = whale_df[whale_df['action'] == 'Buy']\n",
    "sells_w = whale_df[whale_df['action'] == 'Sell']\n",
    "# Size bubbles by xDAI amount (capped for readability)\n",
    "ax.scatter(buys_w['date'], buys_w['effective_price'], c='#2ecc71',\n",
    "           s=[min(x/2000, 300) for x in buys_w['xdai_total']],\n",
    "           alpha=0.8, label='Buy', edgecolors='black', linewidth=0.5, zorder=5)\n",
    "ax.scatter(sells_w['date'], sells_w['effective_price'], c='#e74c3c',\n",
    "           s=[min(x/500, 200) for x in sells_w['xdai_total']],\n",
    "           alpha=0.8, label='Sell', edgecolors='black', linewidth=0.5, zorder=5)\n",
    "ax.plot(abc_sorted['date'], abc_sorted['price_per_token'], color='gray', alpha=0.3, linewidth=0.8, label='ABC market')\n",
    "ax.set_ylabel('Price (xDAI/TEC)')\n",
    "ax.set_title('Whale Trade Prices vs ABC Market Price\\n(bubble size = xDAI volume)')\n",
    "ax.legend(fontsize=9)\n",
    "ax.set_ylim(0, 3.5)\n",
    "\n",
    "# Panel 2: TEC balance over time\n",
    "ax = axes[0, 1]\n",
    "ax.step(pnl_df['date'], pnl_df['tec_balance'], where='post', color='steelblue', linewidth=2)\n",
    "ax.axhline(y=0, color='black', linewidth=0.5)\n",
    "ax.fill_between(pnl_df['date'], pnl_df['tec_balance'], step='post', alpha=0.2, color='steelblue')\n",
    "remaining = pnl_df['tec_balance'].iloc[-1]\n",
    "ax.annotate(f'{remaining:,.0f} TEC remaining\\n(from ABC buys)',\n",
    "           xy=(pnl_df['date'].iloc[-1], remaining),\n",
    "           xytext=(-80, 30), textcoords='offset points', fontsize=9,\n",
    "           arrowprops=dict(arrowstyle='->', color='steelblue'))\n",
    "ax.set_ylabel('TEC Balance (ABC trades only)')\n",
    "ax.set_title('Whale TEC Holdings from ABC Over Time')\n",
    "\n",
    "# Panel 3: Cumulative P&L\n",
    "ax = axes[1, 0]\n",
    "ax.step(pnl_df['date'], pnl_df['pnl'], where='post', color='#e74c3c', linewidth=2)\n",
    "ax.fill_between(pnl_df['date'], pnl_df['pnl'], step='post', alpha=0.2, color='#e74c3c')\n",
    "ax.axhline(y=0, color='black', linewidth=0.5)\n",
    "ax.set_ylabel('Cumulative P&L (xDAI)')\n",
    "ax.set_title('Whale Realized P&L (ignoring unsold TEC)')\n",
    "ax.set_xlabel('Date')\n",
    "\n",
    "# Panel 4: Tribute contribution breakdown\n",
    "ax = axes[1, 1]\n",
    "buy_tribute = buys_w['tribute'].sum()\n",
    "sell_tribute = sells_w['tribute'].sum()\n",
    "total_tribute = buy_tribute + sell_tribute\n",
    "bars = ax.bar(\n",
    "    [f'Entry Tribute (22%)\\n{buy_tribute:,.0f} xDAI', f'Exit Tribute (8-12%)\\n{sell_tribute:,.0f} xDAI'],\n",
    "    [buy_tribute, sell_tribute],\n",
    "    color=['#2ecc71', '#e74c3c'], alpha=0.7, width=0.5\n",
    ")\n",
    "ax.set_ylabel('Tribute Amount (xDAI)')\n",
    "ax.set_title(f'Whale Tribute to Common Pool: {total_tribute:,.0f} xDAI total')\n",
    "\n",
    "for ax in axes.flat:\n",
    "    ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.savefig(f'{DATA}/../snapshots/whale_analysis.png', dpi=150, bbox_inches='tight')\n",
    "plt.show()\n",
    "\n",
    "# Summary stats\n",
    "total_buy_xdai = buys_w['xdai_total'].sum()\n",
    "total_sell_xdai = sells_w['xdai_net'].sum()\n",
    "total_tec_bought = buys_w['tec_amount'].sum()\n",
    "total_tec_sold = sells_w['tec_amount'].sum()\n",
    "tec_remaining = total_tec_bought - total_tec_sold\n",
    "\n",
    "print(f'\\n500K WHALE DEEP DIVE ({WHALE})')\n",
    "print(f'{\"=\" * 65}')\n",
    "print(f'\\nENTRY (2 buys on Jan 25, 2022):')\n",
    "print(f'  Total spent: {total_buy_xdai:,.0f} xDAI (incl {buys_w[\"tribute\"].sum():,.0f} xDAI entry tribute)')\n",
    "print(f'  TEC minted:  {total_tec_bought:,.0f} TEC')\n",
    "print(f'  Avg price:   {total_buy_xdai / total_tec_bought:.4f} xDAI/TEC (all-in)')\n",
    "print(f'  This single purchase was {total_buy_xdai / abc_sorted[abc_sorted[\"action\"]==\"Buy\"][\"Amount\"].sum():.1%} of ALL ABC buy volume')\n",
    "print(f'\\nEXIT (6 sells, May 2022 - Jun 2023):')\n",
    "for _, s in sells_w.iterrows():\n",
    "    print(f'  {s[\"date\"].strftime(\"%Y-%m-%d\")}: {s[\"tec_amount\"]:>10,.0f} TEC @ {s[\"effective_price\"]:.2f} xDAI/TEC = {s[\"xdai_net\"]:>10,.0f} xDAI (exit tribute: {s[\"tribute\"]:,.0f})')\n",
    "print(f'  Total received: {total_sell_xdai:,.0f} xDAI from {total_tec_sold:,.0f} TEC sold')\n",
    "print(f'\\nTRIBUTE CONTRIBUTION:')\n",
    "print(f'  Entry tribute: {buys_w[\"tribute\"].sum():,.0f} xDAI')\n",
    "print(f'  Exit tribute:  {sells_w[\"tribute\"].sum():,.0f} xDAI')\n",
    "print(f'  Total:         {total_tribute:,.0f} xDAI to common pool')\n",
    "print(f'\\nNET POSITION:')\n",
    "print(f'  Realized P&L:  {total_sell_xdai - total_buy_xdai:+,.0f} xDAI ({(total_sell_xdai/total_buy_xdai - 1):+.1%})')\n",
    "print(f'  TEC remaining: {tec_remaining:,.0f} (from ABC) + ~{HATCH_TEC:,} (from Hatch)')\n",
    "print(f'  Current holder rank: #36 with 6,203 TEC')\n",
    "print(f'  Implied disposal: ~{tec_remaining + HATCH_TEC - 6203:,.0f} TEC transferred/sold elsewhere')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9. Key Findings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-03T21:30:51.621808Z",
     "iopub.status.busy": "2026-04-03T21:30:51.621640Z",
     "iopub.status.idle": "2026-04-03T21:30:51.628455Z",
     "shell.execute_reply": "2026-04-03T21:30:51.627332Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "======================================================================\n",
      "ABC & MARKET ANALYSIS — KEY FINDINGS\n",
      "======================================================================\n",
      "\n",
      "BONDING CURVE MECHANICS:\n",
      "  Price peak: 2.94 xDAI\n",
      "  Price final: 0.36 xDAI\n",
      "  Drawdown: -88%\n",
      "  Reserve peak: 1,119,156 xDAI → 111,623\n",
      "\n",
      "STRUCTURAL SELL PRESSURE:\n",
      "  Buy transactions: 398 (15%)\n",
      "  Sell transactions: 2217 (85%)\n",
      "  Sell:Buy ratio: 5.6:1\n",
      "  The ABC experienced persistent, overwhelming sell pressure.\n",
      "\n",
      "TRIBUTE REVENUE:\n",
      "  Total: 329,904 xDAI\n",
      "  This was far too small to sustain common pool operations.\n",
      "\n",
      "HOLDER CONCENTRATION:\n",
      "  723 total holders\n",
      "  Top holder (gideonro.eth): 13.2%\n",
      "  Top 10: 50.7%\n",
      "\n",
      "CRITICAL QUESTIONS:\n",
      "  1. Did the ABC entry tribute discourage buying? (effectively a tax on participation)\n",
      "  2. Did the exit tribute slow selling enough, or just reduce returns?\n",
      "  3. Was the reserve ratio appropriate for the level of volatility?\n",
      "  4. Could the ABC have functioned better with different parameters?\n",
      "  5. Did DEX liquidity create a cheaper exit path that undermined the ABC?\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print('=' * 70)\n",
    "print('ABC & MARKET ANALYSIS — KEY FINDINGS')\n",
    "print('=' * 70)\n",
    "print(f'''\n",
    "BONDING CURVE MECHANICS:\n",
    "  Price peak: {abc_sorted[\"price_per_token\"].max():.2f} xDAI\n",
    "  Price final: {abc_sorted[\"price_per_token\"].iloc[-1]:.2f} xDAI\n",
    "  Drawdown: {(abc_sorted[\"price_per_token\"].iloc[-1] / abc_sorted[\"price_per_token\"].max() - 1):.0%}\n",
    "  Reserve peak: {abc_sorted[\"reserve_balance\"].max():,.0f} xDAI → {abc_sorted[\"reserve_balance\"].iloc[-1]:,.0f}\n",
    "\n",
    "STRUCTURAL SELL PRESSURE:\n",
    "  Buy transactions: {len(buys)} ({len(buys)/(len(buys)+len(sells)):.0%})\n",
    "  Sell transactions: {len(sells)} ({len(sells)/(len(buys)+len(sells)):.0%})\n",
    "  Sell:Buy ratio: {len(sells)/len(buys):.1f}:1\n",
    "  The ABC experienced persistent, overwhelming sell pressure.\n",
    "  \n",
    "TRIBUTE REVENUE:\n",
    "  Total: {tribute_by_action.sum():,.0f} xDAI\n",
    "  This was far too small to sustain common pool operations.\n",
    "\n",
    "HOLDER CONCENTRATION:\n",
    "  {len(holders_clean)} total holders\n",
    "  Top holder ({holders_clean.iloc[0][\"ens_name\"]}): {holders_clean.iloc[0][\"perc\"]:.1%}\n",
    "  Top 10: {top10_pct:.1%}\n",
    "\n",
    "CRITICAL QUESTIONS:\n",
    "  1. Did the ABC entry tribute discourage buying? (effectively a tax on participation)\n",
    "  2. Did the exit tribute slow selling enough, or just reduce returns?\n",
    "  3. Was the reserve ratio appropriate for the level of volatility?\n",
    "  4. Could the ABC have functioned better with different parameters?\n",
    "  5. Did DEX liquidity create a cheaper exit path that undermined the ABC?\n",
    "''')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "TEC Analysis",
   "language": "python",
   "name": "tec-analysis"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
