{ "cells": [ { "cell_type": "markdown", "id": "57068fac", "metadata": {}, "source": [ "# Statistical Arbitrage in Cryptocurrency through Liquidation Cascades\n", "by identifying forced selling events and capturing the subsequent mean reversion.\n", "\n", "Strategy Overview:\n", "- Detects liquidation events using multi-timeframe price drops and volume spikes\n", "- Identifies cascade liquidation patterns across multiple assets\n", "- Enters long positions during panic selling to capture behavioral finance inefficiencies\n", "- Exits when prices revert to fair value or technical conditions change\n", "\n", "\n", "Idea: Use Coinglass liquidation data to selectively detect liquidation cascades and act on them, then hold to generate profit as the prices revert. \n", "\n", "In this run, we get binance data from 2023-01-01 to 2024-01-31; other periods starting from 2020 were used to capture the effects of COVID as well\n", "\n", "Author: Nitish Kaza" ] }, { "cell_type": "code", "execution_count": 1, "id": "4425495e", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\Nitish\\anaconda3\\lib\\site-packages\\scipy\\__init__.py:138: UserWarning: A NumPy version >=1.16.5 and <1.23.0 is required for this version of SciPy (detected version 1.24.4)\n", " warnings.warn(f\"A NumPy version >={np_minversion} and <{np_maxversion} is required for this version of \"\n" ] } ], "source": [ "from binance.client import Client as bnb_client\n", "from datetime import datetime\n", "import pandas as pd, numpy as np\n", "import matplotlib.pyplot as plt\n", "import time\n", "from scipy import stats\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "38491528", "metadata": {}, "outputs": [], "source": [ "client = bnb_client(tld='us') " ] }, { "cell_type": "code", "execution_count": 3, "id": "8d667367", "metadata": {}, "outputs": [], "source": [ "#Data collection function\n", "\n", "st = '2023-01-01'\n", "et = '2024-01-31'\n", "\n", "def get_binance_px(symbol, freq, start_ts=st, end_ts=et):\n", " data = client.get_historical_klines(symbol, freq, start_ts, end_ts)\n", " df = pd.DataFrame(data, columns=[\n", " 'open_time','open','high','low','close','volume','close_time',\n", " 'quote_volume','num_trades','taker_base_volume','taker_quote_volume','ignore'\n", " ])\n", " df['open_time'] = pd.to_datetime(df['open_time'], unit='ms')\n", " df.set_index('open_time', inplace=True)\n", " df[['close','volume']] = df[['close','volume']].astype(float)\n", " return df[['close','volume']]" ] }, { "cell_type": "code", "execution_count": 4, "id": "1fdcdad1", "metadata": {}, "outputs": [], "source": [ "#Assets to be included in the strategy\n", "\n", "univ = [\n", " 'BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT',\n", " 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT', 'DOTUSDT', 'MATICUSDT',\n", " 'LTCUSDT', 'SHIBUSDT', 'TRXUSDT', 'LINKUSDT', 'BCHUSDT',\n", " 'UNIUSDT', 'XLMUSDT', 'ATOMUSDT', 'FILUSDT', 'ETCUSDT'\n", "]\n", "\n", "freq = '1h'\n", "px, vol = {}, {}\n", "\n", "for sym in univ:\n", " try:\n", " df = get_binance_px(sym, freq)\n", " px[sym] = df['close']\n", " vol[sym] = df['volume']\n", " time.sleep(1.5) # avoid API rate limits\n", " except Exception as e:\n", " print(f\"Failed to load {sym}: {e}\")\n", "\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "b015c235", "metadata": {}, "outputs": [], "source": [ "px = pd.DataFrame(px).sort_index()\n", "vol = pd.DataFrame(vol).sort_index()\n", "px = px.reindex(pd.date_range(px.index[0], px.index[-1], freq=freq))\n", "vol = vol.reindex_like(px)\n", "ret = px.pct_change()" ] }, { "cell_type": "markdown", "id": "dfdb4226", "metadata": {}, "source": [ "# LIQUIDATION EVENT DETECTION ALGORITHM\n", " \n", " This function identifies forced liquidation events in cryptocurrency markets by analyzing\n", " price drops, volume spikes, and cascade patterns. The algorithm is designed to capture\n", " behavioral finance phenomena where panic selling creates temporary price dislocations.\n", " \n", " Approach:\n", " 1. Multi-timeframe price analysis to detect different liquidation patterns\n", " 2. Dynamic volume thresholds using rolling percentiles to adapt to market conditions\n", " 3. Cascade detection to identify multi-stage liquidation events\n", " 4. Market-wide confirmation to filter out single-asset noise\n", " \n", " Parameters:\n", " px : Hourly price data for cryptocurrency assets\n", " vol : Hourly volume data for cryptocurrency assets\n", " initial_drop : Threshold for initial liquidation signal (default: 5%)\n", " cascade_drop : Additional drop threshold for cascade detection (default: 3%)\n", " vol_percentile : Volume percentile threshold for confirmation (default: 95th percentile)\n", " min_assets : Minimum number of assets that must signal simultaneously (default: 3)\n", " \n", " Returns:\n", " pd.DataFrame\n", " Boolean matrix indicating liquidation events for each asset at each timestamp\n", " \n", " Strategy Rationale:\n", " ------------------\n", " Liquidation events represent forced selling by leveraged traders, creating temporary\n", " price dislocations below fair value. These events exhibit specific characteristics:\n", " - Sharp price drops over short timeframes\n", " - Abnormally high trading volume\n", " - Contagion effects across correlated assets\n", " - Predictable mean reversion as panic subsides" ] }, { "cell_type": "code", "execution_count": 6, "id": "5f688212", "metadata": {}, "outputs": [], "source": [ "def detect_liquidation_events(px, vol, \n", " initial_drop=0.05, cascade_drop=0.03, \n", " vol_percentile=95, min_assets=3):\n", " crash_signals = pd.DataFrame(index=px.index)\n", " \n", " for sym in px.columns:\n", " ret_1h = px[sym].pct_change()\n", " ret_2h = px[sym].pct_change(periods=2)\n", " \n", " vol_thresh_95 = vol[sym].rolling(168).quantile(0.95) # 7-day 95th percentile\n", " vol_thresh_90 = vol[sym].rolling(168).quantile(0.90) # 7-day 90th percentile\n", " \n", " strong_drop = ret_1h < -initial_drop # 5% drop in 1h\n", " cascade = (ret_2h < -(initial_drop + cascade_drop)) & (ret_1h > -cascade_drop) # cascade pattern\n", " extreme_drop = ret_1h < -(initial_drop * 1.6) \n", " \n", " extreme_vol = vol[sym] > vol_thresh_95\n", " high_vol = vol[sym] > vol_thresh_90\n", " \n", " liquidation_signal = ((strong_drop | extreme_drop) & extreme_vol) | (cascade & high_vol)\n", " \n", " crash_signals[sym] = liquidation_signal\n", " \n", " joint_crashes = crash_signals.sum(axis=1) >= min_assets\n", " return crash_signals.where(joint_crashes, False)" ] }, { "cell_type": "markdown", "id": "d6c8c214", "metadata": {}, "source": [ "Now we identify crashes based on the above function and check if bounce occurs afterward to generate a signal. " ] }, { "cell_type": "code", "execution_count": 7, "id": "62ebbd1b", "metadata": {}, "outputs": [], "source": [ "crashes = detect_liquidation_events(px, vol)" ] }, { "cell_type": "code", "execution_count": 8, "id": "3cd1733a", "metadata": {}, "outputs": [], "source": [ "confirmed_bounce = px.pct_change().shift(-1) > 0\n", "signals = crashes & confirmed_bounce\n", "signals = signals.shift(1)" ] }, { "cell_type": "markdown", "id": "4a9448d5", "metadata": {}, "source": [ "# Returns from trading strategy implementing an adaptive hold period approach based on short-term momentum signals:\n", "\n", "Signal Detection: Identifies trading opportunities when signals are active for specific assets at given timestamps\n", "\n", "Adaptive Hold Period:\n", "If 1-hour forward return > 2%, hold for 1 hour to capture quick gains\n", "Otherwise, hold for 2 hours to allow for longer-term momentum\n", "Transaction Costs: Applies 0.2% transaction cost per trade to account for realistic trading friction\n", "\n", "Return Calculation:\n", "1-Hour Returns: px.pct_change().shift(-1) - forward-looking 1-hour price changes\n", "2-Hour Returns: px.pct_change(2).shift(-2) - forward-looking 2-hour price changes\n", "Net Returns: Gross returns minus transaction costs for performance evaluation\n", "\n", "Data Collection:\n", "The strategy tracks both aggregate performance (strategy_returns) and detailed trade-level information (trade_details) including timestamps, assets, returns, and hold periods for comprehensive analysis.\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "id": "1865826b", "metadata": {}, "outputs": [], "source": [ "strategy_returns = []\n", "trade_details = []\n", "transaction_cost = 0.002\n", "\n", "ret_1h = px.pct_change().shift(-1)\n", "ret_2h = px.pct_change(2).shift(-2)\n", "\n", "for timestamp in signals.index:\n", " if pd.isna(timestamp):\n", " continue\n", " \n", " active_signals = signals.loc[timestamp]\n", " if active_signals.any():\n", " signal_assets = active_signals[active_signals].index.tolist()\n", " \n", " for asset in signal_assets:\n", " if timestamp not in ret_1h.index or timestamp not in ret_2h.index:\n", " continue\n", " \n", " return_1h = ret_1h.loc[timestamp, asset]\n", " return_2h = ret_2h.loc[timestamp, asset]\n", " \n", " if pd.isna(return_1h) or pd.isna(return_2h):\n", " continue\n", " \n", " if return_1h > 0.02:\n", " gross_return = return_1h\n", " hold_period = 1\n", " else:\n", " gross_return = return_2h\n", " hold_period = 2\n", " \n", " net_return = gross_return - transaction_cost\n", " \n", " strategy_returns.append(net_return)\n", " trade_details.append({\n", " 'timestamp': timestamp,\n", " 'asset': asset,\n", " 'gross_return': gross_return,\n", " 'net_return': net_return,\n", " 'hold_period': hold_period\n", " })" ] }, { "cell_type": "code", "execution_count": 10, "id": "d3c84dbe", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Total trades: 98\n", "Sample strategy returns: [ 0.01583591 0.00871081 0.00843219 0.00830098 -0.0004345 0.00225747\n", " 0.00951631 0.01250589 0.00598935 0.00979392]\n", "Max single period return: 0.1911563799168532\n", "Min single period return: -0.027178068577107806\n", "Mean return per period: 0.005174487949764721\n" ] } ], "source": [ "strategy_returns = pd.Series(strategy_returns)\n", "num_trades = len(strategy_returns)\n", "\n", "print(f\"Total trades: {num_trades}\")\n", "print(f\"Sample strategy returns: {strategy_returns.head(10).values}\")\n", "print(f\"Max single period return: {strategy_returns.max()}\")\n", "print(f\"Min single period return: {strategy_returns.min()}\")\n", "print(f\"Mean return per period: {strategy_returns.mean()}\")" ] }, { "cell_type": "code", "execution_count": 11, "id": "e3ab7430", "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "strategy_returns.cumsum().plot(title=\"Liquidation Bounce Strategy PnL\")\n", "plt.xlabel(\"Time\")\n", "plt.ylabel(\"Cumulative Return\")\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 12, "id": "e9434abd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Data period: 2023-01-01 00:00:00 to 2024-01-31 00:00:00\n", "Total days: 395\n", "Years: 1.08\n", "\n", "============================================================\n", "PERFORMANCE METRICS\n", "============================================================\n", "Total Return (sum of trades): 50.71%\n", "Mean Return per Trade: 0.5174%\n", "Annualized Return: 46.89%\n", "Annualized Volatility: 25.96%\n", "Sharpe Ratio (Annualized): 1.806\n", "Max Drawdown: -20.45%\n", "Alpha (Annualized): 0.0049\n", "Beta vs BTC: -0.010\n", "R-squared: 0.000\n", "Number of Trades: 98\n" ] } ], "source": [ "if len(strategy_returns) > 0:\n", " start_date = px.index[0]\n", " end_date = px.index[-1]\n", " total_days = (end_date - start_date).days\n", " n_years = total_days / 365.25 if total_days > 0 else 1\n", "\n", " print(f\"Data period: {start_date} to {end_date}\")\n", " print(f\"Total days: {total_days}\")\n", " print(f\"Years: {n_years:.2f}\")\n", "\n", " total_return = strategy_returns.sum()\n", " mean_return = strategy_returns.mean()\n", " std_return = strategy_returns.std()\n", " trades_per_year = len(strategy_returns) / n_years\n", "\n", " # Annualized return and volatility\n", " annualized_return = mean_return * trades_per_year\n", " annualized_vol = std_return * np.sqrt(trades_per_year)\n", " \n", " # Sharpe ratio\n", " sharpe_ratio = (mean_return / std_return) * np.sqrt(trades_per_year) if std_return > 0 else 0\n", "\n", " # Max drawdown\n", " cumulative_returns = strategy_returns.cumsum()\n", " running_max = cumulative_returns.cummax()\n", " drawdown = cumulative_returns - running_max\n", " max_drawdown = drawdown.min()\n", "\n", " # Alpha/Beta vs BTC\n", " btc_returns = ret['BTCUSDT'].dropna()\n", " strat_ts = pd.Series(0.0, index=px.index)\n", "\n", " for trade in trade_details:\n", " ts = trade['timestamp']\n", " if ts in strat_ts.index:\n", " strat_ts.loc[ts] += trade['net_return']\n", "\n", " aligned = pd.concat([strat_ts, btc_returns], axis=1).dropna()\n", " if len(aligned) > 50:\n", " beta, alpha, r_val, _, _ = stats.linregress(aligned.iloc[:,1], aligned.iloc[:,0])\n", " alpha_annualized = alpha * trades_per_year\n", " r_squared = r_val**2\n", " else:\n", " beta, alpha_annualized, r_squared = 0, 0, 0\n", "\n", " print(\"\\n\" + \"=\"*60)\n", " print(\"PERFORMANCE METRICS\")\n", " print(\"=\"*60)\n", " print(f\"Total Return (sum of trades): {total_return:.2%}\")\n", " print(f\"Mean Return per Trade: {mean_return:.4%}\")\n", " print(f\"Annualized Return: {annualized_return:.2%}\")\n", " print(f\"Annualized Volatility: {annualized_vol:.2%}\")\n", " print(f\"Sharpe Ratio (Annualized): {sharpe_ratio:.3f}\")\n", " print(f\"Max Drawdown: {max_drawdown:.2%}\")\n", " print(f\"Alpha (Annualized): {alpha_annualized:.4f}\")\n", " print(f\"Beta vs BTC: {beta:.3f}\")\n", " print(f\"R-squared: {r_squared:.3f}\")\n", " print(f\"Number of Trades: {len(strategy_returns)}\")\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "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.8.8" } }, "nbformat": 4, "nbformat_minor": 5 }