{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import random\n",
    "\n",
    "from scipy.stats import gamma\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Discounted integral 'Voting'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Simple model proposed for implementing and testing a voting scheme\n",
    "\n",
    "- Assume a dynamic supply of governance tokens accessed by a bonding ETH (linear bonding curve)\n",
    "- Assume this tokens also represent a stake in a revenue generating process\n",
    "- The revenue generating process is has one parameter which is \"governed\" \n",
    "- The revenue generated is random and there is a true \"best parameter\" unknown to the voters which may change\n",
    "- The goal of the 'voting' system is for the selected parameter to trend toward the \"best parameter\" (even if it changes)\n",
    "- In this set up, voting is completely passive, \"votes\" are automatically determined by each agents belief state and counted according to their balance of the 'Tokens' that representing their voting capacity\n",
    "- An agent has the right to change their belief or preference at any time but the effect of their prior beliefs or prefences continues to influence the system, decaying in time according to the forgetfulness parameter\n",
    "- These tokens also represent their stake in the pool of Ether being generated by the revenue process\n",
    "\n",
    "This is a sensor fusion problem -- coordination problem. The environment, the pool of agents, the process, the actions and the system updates have been made mind-numbingly noisy in order to show the effect of the di"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Define the Revenue generating process\n",
    "def revenue(true_best_param,current_voted_param ):\n",
    "    #use an concave function with unique maximum as true_best_param = current_voted_param\n",
    "    base_scale = 1\n",
    "    scale = base_scale*np.exp(-(true_best_param-current_voted_param)**2)\n",
    "    shape = .5\n",
    "    return gamma(scale, shape).rvs()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 7724 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a0e136550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#test the revenue random variable as a function of how right the param is\n",
    "N = 1000\n",
    "bins = 10\n",
    "d11=np.zeros(N)\n",
    "d12=np.zeros(N)\n",
    "d21=np.zeros(N)\n",
    "d110=np.zeros(N)\n",
    "d101=np.zeros(N)\n",
    "for i in range(N):\n",
    "    d11[i] = revenue(1,1)\n",
    "    d12[i] = revenue(1,2)\n",
    "    d21[i] = revenue(2,1)\n",
    "    d110[i] = revenue(1,10)\n",
    "    d101[i] = revenue(10,1)\n",
    "\n",
    "plt.hist(d11, bins, alpha=0.5, label='d11')\n",
    "plt.hist(d12, bins, alpha=0.5, label='d12')\n",
    "plt.hist(d21, bins, alpha=0.5, label='d21')\n",
    "plt.hist(d110, bins, alpha=0.5, label='d110')\n",
    "plt.hist(d101, bins, alpha=0.5, label='d101')\n",
    "\n",
    "plt.legend(loc='upper right')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Lets assuming that all tokens in existence is given by T\n",
    "\n",
    "def bond_mint(eth, Eth, Tokens):\n",
    "    return eth*Tokens/Eth\n",
    "\n",
    "def burn_withdraw(tokens, Eth, Tokens):\n",
    "    return tokens*Eth/Tokens\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#define a set of agents\n",
    "agents = {}\n",
    "\n",
    "#in this passive voting model agents maintain a belief or a preference as part of their state with respect\n",
    "#to the contract the 'token' came from\n",
    "#their belief or preference may be updated by them at will\n",
    "def add_agent(eth, tbparam, Eth, Tokens):\n",
    "    agents[str(len(agents))] = {\"eth\":eth,\n",
    "                           \"tokens\":0,\n",
    "                           \"param_belief\": 2*tbparam*np.random.uniform(),\n",
    "                           \"value_belief\":2*Tokens/Eth*np.random.uniform()}\n",
    "    \n",
    "\n",
    "def agent_acts(a, Eth, Tokens, r, p):\n",
    "    #update value belief\n",
    "    agents[a][\"value_belief\"] = 2*Tokens/Eth*np.random.uniform()\n",
    "    \n",
    "    #update tokens held -- buy or sell -- pretty naive with random belief and random amount\n",
    "    if agents[a][\"value_belief\"]<Tokens/Eth:\n",
    "        urv =np.random.uniform() \n",
    "        burn = agents[a][\"tokens\"]*urv\n",
    "        eth_out = burn_withdraw(burn, Eth, Tokens)\n",
    "        \n",
    "        agents[a][\"tokens\"] = agents[a][\"tokens\"]-burn\n",
    "        agents[a][\"eth\"] = agents[a][\"eth\"] + eth_out\n",
    "        Tokens = Tokens - burn\n",
    "        Eth = Eth - eth_out\n",
    "        \n",
    "    else:\n",
    "        \n",
    "        urv =np.random.uniform() \n",
    "        bond =agents[a][\"eth\"]*urv\n",
    "        mint = bond_mint(bond, Eth, Tokens)\n",
    "        \n",
    "        agents[a][\"tokens\"] = agents[a][\"tokens\"]+mint\n",
    "        agents[a][\"eth\"] = agents[a][\"eth\"] -bond\n",
    "        Tokens = Tokens + mint\n",
    "        Eth = Eth + bond\n",
    "        \n",
    "    #update believe by taking a tiny step along slope of observations\n",
    "    #only assumption is that the agent roughly estimates the direction of better returns from observed revenue events\n",
    "    agents[a][\"param_belief\"] = np.max([0,agents[a][\"param_belief\"] + np.random.uniform()*np.polyfit(p, r, 1)[0]])\n",
    "\n",
    "    #print(agents[a][\"value_belief\"])\n",
    "    return Eth, Tokens, agents[a]\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "K= 50000\n",
    "alpha=.95 #forgetfulness\n",
    "true_best_param = 5*np.ones(K) \n",
    "#throw a step change in there to mix things up\n",
    "#true_best_param[:int(K/2)] =.5*true_best_param[:int(K/2)]\n",
    "\n",
    "\n",
    "E=np.zeros(K)\n",
    "T=np.zeros(K)\n",
    "\n",
    "A=np.zeros(K)\n",
    "\n",
    "Eth =1\n",
    "Tokens =100\n",
    "E[0] = Eth\n",
    "T[0] = Tokens\n",
    "A[0] = 1\n",
    "\n",
    "agents = {}\n",
    "add_agent(gamma(5,1).rvs(), true_best_param[0], Eth, Tokens)\n",
    "\n",
    "votes=np.zeros(K)\n",
    "count=np.zeros(K)\n",
    "param=np.zeros(K)\n",
    "rev=np.zeros(K)\n",
    "\n",
    "n = np.zeros(K)\n",
    "n[0]=1\n",
    "\n",
    "for k in range(1,K):\n",
    "    Eth = E[k-1]\n",
    "    Tokens = T[k-1]\n",
    "    #resolve governance -- what is the belief of the param\n",
    "    votes[k] = (np.sum([agents[a][\"tokens\"]*agents[a][\"param_belief\"] for a in list(agents.keys())])+ alpha*votes[k-1])\n",
    "    count[k] = (np.sum([agents[a][\"tokens\"] for a in list(agents.keys())])+ alpha*count[k-1])\n",
    "    if count[k]>0:\n",
    "        param[k] = votes[k]/count[k]\n",
    "    else:\n",
    "        param[k] = 1\n",
    "    rev[k] = revenue(true_best_param[k],param[k])\n",
    "    Eth = Eth+rev[k]\n",
    "    \n",
    "    #new agents join\n",
    "    if np.random.uniform()< np.log10(K/k):\n",
    "        new = int(2*np.random.uniform())\n",
    "        for i in range(new):\n",
    "            add_agent(gamma(5,1).rvs(), true_best_param[k], Eth, Tokens)\n",
    "    n[k]=len(agents)\n",
    "        \n",
    "    #pick some to update on the order of log of agents\n",
    "    active = random.sample(list(agents.keys()), int(np.log2(len(agents))))\n",
    "    for a in active:\n",
    "        Eth, Tokens, val = agent_acts(a, Eth, Tokens, rev[:k+1], param[:k+1])\n",
    "        agents[a] = val\n",
    "    \n",
    "    A[k] = len(active)\n",
    "    E[k] = Eth\n",
    "    T[k] = Tokens\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "49999"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "k"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1a16fbb748>]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 12832 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a13c03eb8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(range(K),n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([2.7333e+04, 1.6130e+04, 4.6390e+03, 1.3580e+03, 3.7600e+02,\n",
       "        1.1900e+02, 3.2000e+01, 9.0000e+00, 3.0000e+00, 1.0000e+00]),\n",
       " array([ 0.        ,  1.2107594 ,  2.4215188 ,  3.63227819,  4.84303759,\n",
       "         6.05379699,  7.26455639,  8.47531578,  9.68607518, 10.89683458,\n",
       "        12.10759398]),\n",
       " <a list of 10 Patch objects>)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 6048 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a13be3898>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist(rev)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,\n",
       "        0.00000000e+00, 0.00000000e+00, 1.66618863e-04, 1.11095012e-02,\n",
       "        5.99890619e-01, 6.28179818e+00, 2.94059390e+00, 1.65366177e-01,\n",
       "        2.15563329e-04, 2.27537984e-04, 7.02105404e-05, 2.80842162e-04,\n",
       "        7.02105404e-05, 0.00000000e+00, 2.10631621e-04]),\n",
       " array([-1.00000000e+00, -9.00000000e-01, -8.00000000e-01, -7.00000000e-01,\n",
       "        -6.00000000e-01, -5.00000000e-01, -4.00000000e-01, -3.00000000e-01,\n",
       "        -2.00000000e-01, -1.00000000e-01, -2.22044605e-16,  1.00000000e-01,\n",
       "         2.00000000e-01,  3.00000000e-01,  4.00000000e-01,  5.00000000e-01,\n",
       "         6.00000000e-01,  7.00000000e-01,  8.00000000e-01,  9.00000000e-01]),\n",
       " <a list of 19 Patch objects>)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 5072 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1083741d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist((true_best_param-param)/true_best_param, bins=(np.arange(-1,1,.1)), density=True, alpha=.5)\n",
    "plt.hist((true_best_param-param)/true_best_param, bins=(np.arange(-1,1,.1)), density=True, weights = rev, alpha=.25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,0,'Number of Agents')"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 20140 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a1af7d128>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.semilogy(n, np.abs(true_best_param - param)/true_best_param, n, .1*np.ones(K,))\n",
    "plt.title(\"Logscale absolute percent error\")\n",
    "#plt.labels([\"error\", \"10% reference\"])\n",
    "plt.xlabel(\"Number of Agents\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,0,'Number of Agents')"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 19432 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a1e061c50>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.loglog(n, np.abs(true_best_param - param)/true_best_param, n, .1*np.ones(K,))\n",
    "plt.title(\"Logscale absolute percent error\")\n",
    "#plt.labels([\"error\", \"10% reference\"])\n",
    "plt.xlabel(\"Number of Agents\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5,0,'Block')"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 18516 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a1e007550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.loglog(range(K), np.abs(true_best_param - param)/true_best_param, range(K), .1*np.ones(K,))\n",
    "plt.title(\"Logscale absolute percent error\")\n",
    "#plt.labels([\"error\", \"10% reference\"])\n",
    "plt.xlabel(\"Block\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1a1a283860>,\n",
       " <matplotlib.lines.Line2D at 0x1a1a2837b8>]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 10120 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a1a2d2f28>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(range(K), true_best_param, range(K), param )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1a0e16c7b8>]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 11088 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a17f053c8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(range(K), E/T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1a1c1ecd68>,\n",
       " <matplotlib.lines.Line2D at 0x1a1c313550>]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 14968 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a1b824588>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.loglog(range(K), E, range(K), T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x1a1953e710>]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 11940 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a17a1fc88>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(E, T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x1a19263748>"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 10028 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a17c748d0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.scatter(param,rev)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4, 6, 0, 4]"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 36832 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a181e2630>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sns.kdeplot(param,rev)\n",
    "plt.axis([4,6,0,4])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<seaborn.axisgrid.JointGrid at 0x1a18f80908>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 26752 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1a18f80828>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sns.jointplot(param, rev, kind=\"kde\", size=7, space=0, xlim=[4,6],ylim=[0,5])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
