From f49e5abb4eaf25522a14573cfbca60a72d461f65 Mon Sep 17 00:00:00 2001 From: Kasper Johansson Date: Thu, 29 Feb 2024 09:13:41 -0800 Subject: [PATCH] fixed simulate bug --- cvx/stat_arb/ccp.py | 2 +- experiments/moving_midpoint.ipynb | 425 +++++++++++++++--------------- experiments/utils.py | 7 +- 3 files changed, 219 insertions(+), 215 deletions(-) diff --git a/cvx/stat_arb/ccp.py b/cvx/stat_arb/ccp.py index 63dc933..b666ae1 100644 --- a/cvx/stat_arb/ccp.py +++ b/cvx/stat_arb/ccp.py @@ -436,7 +436,7 @@ def get_q(self, prices: pd.DataFrame, mu=None, T_max=500): ) q[exit_index + exit_length :] = 0 - q[-1] = 0 + q.iloc[-1] = 0 exit_trigger = min(exit_index, len(q) - 1) exit_trigger = q.index[exit_trigger] diff --git a/experiments/moving_midpoint.ipynb b/experiments/moving_midpoint.ipynb index ac4a9a0..cd24b4c 100644 --- a/experiments/moving_midpoint.ipynb +++ b/experiments/moving_midpoint.ipynb @@ -17,208 +17,6 @@ "%autoreload 2" ] }, - { - "cell_type": "code", - "execution_count": 99, - "metadata": {}, - "outputs": [], - "source": [ - "match_fractions = []\n", - "\n", - "for res in results:\n", - " stat_arb = res.stat_arb\n", - "\n", - " match_fractions.append(matching_sector(stat_arb))\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "metadata": {}, - "outputs": [], - "source": [ - "indices = means.sort_values(ascending=False).index[500:]\n", - "\n", - "match_fractions2 = []\n", - "\n", - "for ind in indices:\n", - " res = results[ind]\n", - " stat_arb = res.stat_arb\n", - "\n", - " match_fractions2.append(matching_sector(stat_arb))" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.515285265049416" - ] - }, - "execution_count": 75, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.mean(match_fractions2)" - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.5261135634028893" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.mean(match_fractions)" - ] - }, - { - "cell_type": "code", - "execution_count": 59, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "INTEL CORP, Manufacturing\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "MASTERCARD INC, Professional, Scientific, and Technical Services\n", - "TEVA PHARMACEUTICAL INDS LTD, Manufacturing\n", - "CELGENE CORP, Professional, Scientific, and Technical Services\n", - "NORFOLK SOUTHERN CORP, Transportation and Warehousing\n", - "\n", - "\n", - "AMAZON COM INC, Retail Trade\n", - "ALTABA INC, Professional, Scientific, and Technical Services\n", - "MARRIOTT INTERNATIONAL INC NEW, Accommodation and Food Services\n", - "T I M PARTICIPACOES S A, Information\n", - "PHILLIPS 66, Manufacturing\n", - "\n", - "\n", - "MICROSOFT CORP, Information\n", - "EXXON MOBIL CORP, Manufacturing\n", - "INTEL CORP, Manufacturing\n", - "TORONTO DOMINION BANK ONT, Finance and Insurance\n", - "GENERAL ELECTRIC CO, Manufacturing\n", - "NORFOLK SOUTHERN CORP, Transportation and Warehousing\n", - "\n", - "\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "MASTERCARD INC, Professional, Scientific, and Technical Services\n", - "TEVA PHARMACEUTICAL INDS LTD, Manufacturing\n", - "CELGENE CORP, Professional, Scientific, and Technical Services\n", - "\n", - "\n", - "INTEL CORP, Manufacturing\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "MASTERCARD INC, Professional, Scientific, and Technical Services\n", - "TEVA PHARMACEUTICAL INDS LTD, Manufacturing\n", - "CELGENE CORP, Professional, Scientific, and Technical Services\n", - "NORFOLK SOUTHERN CORP, Transportation and Warehousing\n", - "\n", - "\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "PAYPAL HOLDINGS INC, Administrative and Support and Waste Management and Remediation Services\n", - "SYSCO CORP, Wholesale Trade\n", - "\n", - "\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "PAYPAL HOLDINGS INC, Administrative and Support and Waste Management and Remediation Services\n", - "CAPITAL ONE FINANCIAL CORP, Finance and Insurance\n", - "SYSCO CORP, Wholesale Trade\n", - "\n", - "\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "E M C CORP MA, Manufacturing\n", - "MASTERCARD INC, Professional, Scientific, and Technical Services\n", - "U B S AG, Finance and Insurance\n", - "TEVA PHARMACEUTICAL INDS LTD, Manufacturing\n", - "CELGENE CORP, Professional, Scientific, and Technical Services\n", - "EOG RESOURCES INC, Mining, Quarrying, and Oil and Gas Extraction\n", - "\n", - "\n", - "COMCAST CORP NEW, Information\n", - "GENERAL MOTORS CO, Manufacturing\n", - "REGENERON PHARMACEUTICALS INC, Manufacturing\n", - "N X P SEMICONDUCTORS N V, Manufacturing\n", - "\n", - "\n", - "INTEL CORP, Manufacturing\n", - "BERKSHIRE HATHAWAY INC DEL, Finance and Insurance\n", - "MASTERCARD INC, Professional, Scientific, and Technical Services\n", - "U B S AG, Finance and Insurance\n", - "TEVA PHARMACEUTICAL INDS LTD, Manufacturing\n", - "CELGENE CORP, Professional, Scientific, and Technical Services\n", - "\n", - "\n" - ] - } - ], - "source": [ - "for index in means.sort_values(ascending=False).tail(10).index:\n", - " stat_arb = results[index].stat_arb\n", - " stat_arb_names(stat_arb)\n", - "\n", - " print(\"\\n\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.0269827052012885" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "means[index]" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WELLS FARGO & CO NEW\n", - "CITIGROUP INC\n", - "MASTERCARD INC\n", - "DEUTSCHE BANK A G\n", - "PRECISION CASTPARTS CORP\n" - ] - } - ], - "source": [] - }, { "cell_type": "code", "execution_count": 2, @@ -264,7 +62,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -322,7 +120,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -330,13 +128,10 @@ "output_type": "stream", "text": [ "\n", - "Portfolio went bust at 2013-01-18 00:00:00\n", + "Portfolio exited early at 2013-01-18 00:00:00\n", "bust_sort: 1\n", "\n", - "Portfolio went bust at 2020-03-16 00:00:00\n", - "bust_sort: 1\n", - "\n", - "Portfolio went bust at 2021-10-12 00:00:00\n", + "Portfolio exited early at 2020-03-16 00:00:00\n", "bust_sort: 1\n" ] } @@ -363,14 +158,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 712/712 [00:38<00:00, 18.34it/s]\n" + "100%|██████████| 712/712 [00:28<00:00, 25.12it/s]\n" ] } ], @@ -378,6 +173,214 @@ "stat_arb_metrics = metrics(portfolios_after_cost, results)" ] }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average number of assets per SA: 5.3\n", + "Median number of assets per SA: 5.0\n", + "Number of unique SAs: 712\n", + "Mean profit: 1.10\n", + "Median profit: 0.80\n", + "Std profit: 3.03\n", + "75th percentile: 2.07\n", + "25th percentile: -0.24\n", + "Fraction profitable: 0.70\n", + "Average minimum cumulative profit: -1.76\n", + "\n", + "Mean return\n", + "Mean return: 0.15\n", + "Median return: 0.12\n", + "75th percentile return: 0.24\n", + "25th percentile return: 0.03\n", + "Fraction positive: 0.81\n", + "\n", + "Risk\n", + "Mean risk: 0.20\n", + "Median risk: 0.15\n", + "75th percentile risk: 0.25\n", + "25th percentile risk: 0.09\n", + "\n", + "Sharpe\n", + "Average Sharpe: 0.84\n", + "Median Sharpe: 0.88\n", + "75th percentile Sharpe: 1.52\n", + "25th percentile Sharpe: 0.21\n", + "Fraction positive: 0.81\n", + "\n", + "Drawdown\n", + "Mean drawdown: 0.12\n", + "Median drawdown: 0.09\n", + "75th percentile drawdown: 0.15\n", + "25th percentile drawdown: 0.05\n" + ] + } + ], + "source": [ + "avg_lens = np.mean(lens)\n", + "median_len = np.median(lens)\n", + "n_unique_stat_arbs = n_stat_arbs\n", + "\n", + "means = stat_arb_metrics.means\n", + "stdevs = stat_arb_metrics.stdevs\n", + "sharpes = stat_arb_metrics.sharpes\n", + "profits = stat_arb_metrics.profits\n", + "min_navs = stat_arb_metrics.min_navs\n", + "min_cum_prof = stat_arb_metrics.min_cum_prof\n", + "drawdowns = stat_arb_metrics.drawdowns\n", + "\n", + "print(f\"Average number of assets per SA: {avg_lens:.1f}\")\n", + "print(f\"Median number of assets per SA: {median_len:.1f}\")\n", + "print(f\"Number of unique SAs: {n_unique_stat_arbs}\")\n", + "print(f\"Mean profit: {profits.mean():.2f}\")\n", + "print(f\"Median profit: {profits.median():.2f}\")\n", + "print(f\"Std profit: {profits.std():.2f}\")\n", + "print(f\"75th percentile: {profits.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile: {profits.quantile(0.25):.2f}\")\n", + "print(f\"Fraction profitable: {(profits > 0).mean():.2f}\")\n", + "print(f\"Average minimum cumulative profit: {min_cum_prof.mean():.2f}\")\n", + "\n", + "print(\"\\nMean return\")\n", + "print(f\"Mean return: {means.mean():.2f}\")\n", + "print(f\"Median return: {means.median():.2f}\")\n", + "# print(f\"Std return: {means.std():.2f}\")\n", + "print(f\"75th percentile return: {means.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile return: {means.quantile(0.25):.2f}\")\n", + "print(f\"Fraction positive: {(means > 0).mean():.2f}\")\n", + "\n", + "\n", + "print(\"\\nRisk\")\n", + "print(f\"Mean risk: {stdevs.mean():.2f}\")\n", + "print(f\"Median risk: {stdevs.median():.2f}\")\n", + "# print(f\"Std risk: {stdevs.std():.2f}\")\n", + "print(f\"75th percentile risk: {stdevs.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile risk: {stdevs.quantile(0.25):.2f}\")\n", + "\n", + "print(\"\\nSharpe\")\n", + "print(f\"Average Sharpe: {sharpes.mean():.2f}\")\n", + "print(f\"Median Sharpe: {sharpes.median():.2f}\")\n", + "# print(f\"Std Sharpe: {sharpes.std():.2f}\")\n", + "print(f\"75th percentile Sharpe: {sharpes.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile Sharpe: {sharpes.quantile(0.25):.2f}\")\n", + "print(f\"Fraction positive: {(sharpes > 0).mean():.2f}\")\n", + "\n", + "print(\"\\nDrawdown\")\n", + "print(f\"Mean drawdown: {drawdowns.mean():.2f}\")\n", + "print(f\"Median drawdown: {drawdowns.median():.2f}\")\n", + "# print(f\"Std drawdown: {drawdowns.std():.2f}\")\n", + "print(f\"75th percentile drawdown: {drawdowns.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile drawdown: {drawdowns.quantile(0.25):.2f}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average number of assets per SA: 5.3\n", + "Median number of assets per SA: 5.0\n", + "Number of unique SAs: 712\n", + "Mean profit: 1.10\n", + "Median profit: 0.80\n", + "Std profit: 3.03\n", + "75th percentile: 2.07\n", + "25th percentile: -0.24\n", + "Fraction profitable: 0.70\n", + "Average minimum cumulative profit: -1.76\n", + "\n", + "Mean return\n", + "Mean return: 0.15\n", + "Median return: 0.12\n", + "75th percentile return: 0.24\n", + "25th percentile return: 0.03\n", + "Fraction positive: 0.81\n", + "\n", + "Risk\n", + "Mean risk: 0.20\n", + "Median risk: 0.15\n", + "75th percentile risk: 0.25\n", + "25th percentile risk: 0.09\n", + "\n", + "Sharpe\n", + "Average Sharpe: 0.84\n", + "Median Sharpe: 0.88\n", + "75th percentile Sharpe: 1.52\n", + "25th percentile Sharpe: 0.21\n", + "Fraction positive: 0.81\n", + "\n", + "Drawdown\n", + "Mean drawdown: 0.12\n", + "Median drawdown: 0.09\n", + "75th percentile drawdown: 0.15\n", + "25th percentile drawdown: 0.05\n" + ] + } + ], + "source": [ + "avg_lens = np.mean(lens)\n", + "median_len = np.median(lens)\n", + "n_unique_stat_arbs = n_stat_arbs\n", + "\n", + "means = stat_arb_metrics.means\n", + "stdevs = stat_arb_metrics.stdevs\n", + "sharpes = stat_arb_metrics.sharpes\n", + "profits = stat_arb_metrics.profits\n", + "min_navs = stat_arb_metrics.min_navs\n", + "min_cum_prof = stat_arb_metrics.min_cum_prof\n", + "drawdowns = stat_arb_metrics.drawdowns\n", + "\n", + "print(f\"Average number of assets per SA: {avg_lens:.1f}\")\n", + "print(f\"Median number of assets per SA: {median_len:.1f}\")\n", + "print(f\"Number of unique SAs: {n_unique_stat_arbs}\")\n", + "print(f\"Mean profit: {profits.mean():.2f}\")\n", + "print(f\"Median profit: {profits.median():.2f}\")\n", + "print(f\"Std profit: {profits.std():.2f}\")\n", + "print(f\"75th percentile: {profits.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile: {profits.quantile(0.25):.2f}\")\n", + "print(f\"Fraction profitable: {(profits > 0).mean():.2f}\")\n", + "print(f\"Average minimum cumulative profit: {min_cum_prof.mean():.2f}\")\n", + "\n", + "print(\"\\nMean return\")\n", + "print(f\"Mean return: {means.mean():.2f}\")\n", + "print(f\"Median return: {means.median():.2f}\")\n", + "# print(f\"Std return: {means.std():.2f}\")\n", + "print(f\"75th percentile return: {means.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile return: {means.quantile(0.25):.2f}\")\n", + "print(f\"Fraction positive: {(means > 0).mean():.2f}\")\n", + "\n", + "\n", + "print(\"\\nRisk\")\n", + "print(f\"Mean risk: {stdevs.mean():.2f}\")\n", + "print(f\"Median risk: {stdevs.median():.2f}\")\n", + "# print(f\"Std risk: {stdevs.std():.2f}\")\n", + "print(f\"75th percentile risk: {stdevs.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile risk: {stdevs.quantile(0.25):.2f}\")\n", + "\n", + "print(\"\\nSharpe\")\n", + "print(f\"Average Sharpe: {sharpes.mean():.2f}\")\n", + "print(f\"Median Sharpe: {sharpes.median():.2f}\")\n", + "# print(f\"Std Sharpe: {sharpes.std():.2f}\")\n", + "print(f\"75th percentile Sharpe: {sharpes.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile Sharpe: {sharpes.quantile(0.25):.2f}\")\n", + "print(f\"Fraction positive: {(sharpes > 0).mean():.2f}\")\n", + "\n", + "print(\"\\nDrawdown\")\n", + "print(f\"Mean drawdown: {drawdowns.mean():.2f}\")\n", + "print(f\"Median drawdown: {drawdowns.median():.2f}\")\n", + "# print(f\"Std drawdown: {drawdowns.std():.2f}\")\n", + "print(f\"75th percentile drawdown: {drawdowns.quantile(0.75):.2f}\")\n", + "print(f\"25th percentile drawdown: {drawdowns.quantile(0.25):.2f}\")\n" + ] + }, { "cell_type": "code", "execution_count": 9, diff --git a/experiments/utils.py b/experiments/utils.py index e24533f..0994d16 100644 --- a/experiments/utils.py +++ b/experiments/utils.py @@ -180,9 +180,10 @@ def simulate(res, portfolio, trading_cost_model, lev_fraction): ) navs = portfolio_temp.nav - positions = portfolio_temp.stocks.abs() * portfolio_temp.prices - long_positions = positions[positions > 0].sum(axis=1) - short_positions = positions[positions < 0].sum(axis=1) + positions = portfolio_temp.stocks + absolute_notionals = positions.abs() * portfolio_temp.prices + long_positions = absolute_notionals[positions > 0].sum(axis=1) + short_positions = absolute_notionals[positions < 0].sum(axis=1) cash_position = portfolio_temp.cash cash0 = cash_position.iloc[0]