Skip to content

Instantly share code, notes, and snippets.

@anthonytxie
Last active December 6, 2018 00:20
Show Gist options
  • Save anthonytxie/ad1f493ea9bc36bd86a81697d5fc7493 to your computer and use it in GitHub Desktop.
Save anthonytxie/ad1f493ea9bc36bd86a81697d5fc7493 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"# Define percentages I want in floats\n",
"from datetime import datetime, timedelta\n",
"import pandas as pd\n",
"import numpy as np \n",
"from pandas.tseries.offsets import *\n",
"\n",
"df = pd.read_csv('/price-history.csv', index_col=0)\n",
"df.index = pd.to_datetime(df.index)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"def get_n_random_assets(n, df): \n",
" return pd.concat([df.sample(n=n-1, axis=1), btc_df], axis=1)\n",
"\n",
"\n",
"def gen_rebalancing_dates(date, end_date, rebalancing_period):\n",
" rebalancing_periods = []\n",
" while date < end_date:\n",
" date = date + rebalancing_period\n",
" rebalancing_periods.append(date)\n",
" return rebalancing_periods\n",
"\n",
"def annualize_returns(series, years):\n",
" return (((series[-1]/ series[0])) ** (1/years))-1\n",
"\n",
"\n",
"def set_initial_allocation(tickers):\n",
" allocation = {}\n",
" for ticker in tickers:\n",
" allocation[ticker] = 1/len(tickers)\n",
" return pd.Series(allocation)\n",
"\n",
"def set_min_trading_sizes(tickers, min_btc_amount):\n",
" trading_limits_btc = {}\n",
" for ticker in tickers:\n",
" trading_limits_btc[ticker] = min_btc_amount\n",
" return pd.Series(trading_limits_btc)\n",
" \n",
"\n",
"def calc_returns(df, period ):\n",
" returns = {}\n",
" tickers = df.columns \n",
" for ticker in tickers:\n",
" returns[ticker] = annualize_returns(df[ticker], period)\n",
" return returns\n",
"\n",
"def calc_max_return_difference(df, years):\n",
" max_return = 0\n",
" for column in df:\n",
" returns = annualize_returns(df[column], years)\n",
" if returns > max_return:\n",
" max_return = returns\n",
" return max_return\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"def rebalance (df, rebalancing_period, increment_period, starting_amount):\n",
" \n",
" starting_date = df.index[0]\n",
" output = {'date': [starting_date], 'value': [starting_amount], 'fees': [0], 'failed transactions':[0], 'successful transactions': [0]}\n",
" date = df.index[1]\n",
" end_date = df.index[-1]\n",
" years = (end_date - starting_date).days / 365\n",
" \n",
" tickers = df.columns\n",
" tick_size = set_min_trading_sizes(tickers, 0.001)\n",
" target_allocation = set_initial_allocation(tickers)\n",
" \n",
" amount = starting_amount\n",
" allocation = target_allocation * amount\n",
" rebalancing_array = gen_rebalancing_dates(starting_date, end_date, rebalancing_period)\n",
" \n",
" no_rebalance_end_value = ((df.loc[end_date] / df.loc[date]) * allocation).sum()\n",
" drifting_return = (((no_rebalance_end_value / amount)) ** (1/years)) - 1\n",
" \n",
" while date < end_date:\n",
" # if it is a rebalance date\n",
" if date in rebalancing_array:\n",
" previous_price = df.loc[date - increment_period]\n",
" current_price = df.loc[date]\n",
" current_alloc = ((current_price / previous_price) * allocation)\n",
" new_alloc = current_alloc.sum() * target_allocation\n",
" trading_limits_usd = tick_size * df.loc[date]['bitcoin']\n",
" alloc_diff = current_alloc - new_alloc\n",
" coins_to_sell = alloc_diff[alloc_diff > 0]\n",
" coins_to_sell_minus_btc = coins_to_sell[coins_to_sell.index !='bitcoin']\n",
" increments_to_sell = coins_to_sell_minus_btc // trading_limits_usd\n",
" failed_sells = np.count_nonzero(increments_to_sell < 1 & pd.notna(increments_to_sell))\n",
" successful_sells = np.count_nonzero([increments_to_sell > 1])\n",
" amounts_to_sell = increments_to_sell * trading_limits_usd\n",
" sell_alloc = amounts_to_sell.fillna(0)\n",
" intermittent_alloc = current_alloc - sell_alloc\n",
" sell_fees = sell_alloc.sum() * 0.001 #fee %\n",
" intermittent_alloc['bitcoin'] = intermittent_alloc['bitcoin'] + sell_alloc.sum() - sell_fees\n",
" new_alloc_diff = new_alloc - intermittent_alloc \n",
" coins_to_buy = new_alloc_diff[new_alloc_diff > 0]\n",
" coins_to_buy_no_btc = coins_to_buy[coins_to_buy.index !='bitcoin']\n",
" increments_to_buy = coins_to_buy_no_btc // trading_limits_usd\n",
" successful_buys = np.count_nonzero([increments_to_buy > 1])\n",
" failed_buys = np.count_nonzero([increments_to_buy < 1 & pd.notna(increments_to_buy)])\n",
" amounts_to_buy = increments_to_buy * trading_limits_usd\n",
" buy_alloc = amounts_to_buy.fillna(0)\n",
" buy_fees_alloc = buy_alloc * 0.001 #fee %\n",
" final_alloc = intermittent_alloc + buy_alloc - buy_fees_alloc\n",
" final_alloc['bitcoin'] = final_alloc['bitcoin'] - buy_alloc.sum()\n",
" total_fees = buy_fees_alloc.sum() + sell_fees\n",
" allocation = final_alloc\n",
" failed_trades = failed_sells + failed_buys \n",
" successful_trades = successful_sells + successful_buys\n",
" output['fees'].append(total_fees)\n",
" output['date'].append(date)\n",
" output['value'].append(allocation.sum()) \n",
" output['failed transactions'].append(failed_trades)\n",
" output['successful transactions'].append(successful_trades)\n",
" date += increment_period \n",
" # if it is not a rebalance date\n",
" else:\n",
" previous_price = df.loc[date - increment_period]\n",
" current_price = df.loc[date]\n",
" allocation = ((current_price / previous_price) * allocation)\n",
" amount = allocation.sum()\n",
" output['fees'].append(0)\n",
" output['date'].append(date)\n",
" output['value'].append(amount)\n",
" output['failed transactions'].append(0)\n",
" output['successful transactions'].append(0)\n",
" date += increment_period \n",
" \n",
" rebalancing_df = pd.DataFrame(output).set_index('date')\n",
" max_return_difference = calc_max_return_difference(df, years)\n",
" rebalancing_return = annualize_returns(rebalancing_df['value'], years)\n",
" total_fees = rebalancing_df['fees'].sum()\n",
" failed_trades = rebalancing_df['failed transactions'].sum()\n",
" successful_trades = rebalancing_df['successful transactions'].sum()\n",
" successful_trade_percentage = successful_trades / (successful_trades + failed_trades)\n",
" return {\n",
" 'Return Difference': max_return_difference *100,\n",
" 'Rebalancing Bonus' : (rebalancing_return *100) - (drifting_return *100),\n",
" 'Rebalanced End Value': rebalancing_df['value'][-1] ,\n",
" 'Total Fees': total_fees,\n",
" 'Successful Trading Percentage': successful_trade_percentage,\n",
" 'Tickers': tickers.values,\n",
" \n",
" }\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"rebalancing period 1 num_asset 10 simulation 1\n",
"rebalancing period 1 num_asset 10 simulation 2\n"
]
}
],
"source": [
"evaluate_dict = {'Return Difference': [], 'Rebalancing Bonus': [], 'Rebalanced End Value': [], 'Total Fees': [], 'Successful Trading Percentage': [], 'Rebalancing Period': [], 'Tickers': [] }\n",
"rebalancing_periods = []\n",
"\n",
"# Number of simulations\n",
"for simulation in range(1,3):\n",
" random_assets = get_n_random_assets(10, df)\n",
" for rebalancing_period in range(1,2):\n",
" print('rebalancing period', ' ', rebalancing_period, ' num_asset ', 10, 'simulation', ' ', simulation)\n",
" result = rebalance(random_assets, pd.Timedelta(days=rebalancing_period), pd.Timedelta(days=1), 1000)\n",
" evaluate_dict['Return Difference'].append(result['Return Difference'])\n",
" evaluate_dict['Rebalancing Bonus'].append(result['Rebalancing Bonus'])\n",
" evaluate_dict['Rebalanced End Value'].append(result['Rebalanced End Value'])\n",
" evaluate_dict['Total Fees'].append(result['Total Fees'])\n",
" evaluate_dict['Successful Trading Percentage'].append(result['Successful Trading Percentage'])\n",
" evaluate_dict['Rebalancing Period'].append(rebalancing_period)\n",
" evaluate_dict['Tickers'].append(str(result['Tickers']))\n",
"evaluate_df = pd.DataFrame(evaluate_dict)\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Return Difference</th>\n",
" <th>Rebalancing Bonus</th>\n",
" <th>Rebalanced End Value</th>\n",
" <th>Total Fees</th>\n",
" <th>Successful Trading Percentage</th>\n",
" <th>Rebalancing Period</th>\n",
" <th>Tickers</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>31.2643</td>\n",
" <td>71.456036</td>\n",
" <td>1400.901757</td>\n",
" <td>83.169877</td>\n",
" <td>0.555422</td>\n",
" <td>1</td>\n",
" <td>['revain' 'dent' 'aion' 'enigma' 'enjin-coin' ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0.0000</td>\n",
" <td>13.686982</td>\n",
" <td>463.785862</td>\n",
" <td>12.038766</td>\n",
" <td>0.072878</td>\n",
" <td>1</td>\n",
" <td>['enigma' 'vertcoin' 'zencash' 'monero' 'qtum'...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Return Difference Rebalancing Bonus Rebalanced End Value Total Fees \\\n",
"0 31.2643 71.456036 1400.901757 83.169877 \n",
"1 0.0000 13.686982 463.785862 12.038766 \n",
"\n",
" Successful Trading Percentage Rebalancing Period \\\n",
"0 0.555422 1 \n",
"1 0.072878 1 \n",
"\n",
" Tickers \n",
"0 ['revain' 'dent' 'aion' 'enigma' 'enjin-coin' ... \n",
"1 ['enigma' 'vertcoin' 'zencash' 'monero' 'qtum'... "
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"evaluate_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"anaconda-cloud": {},
"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.6.4"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment