In [1]:
%load_ext autoreload

In [2]:
autoreload 2

In [3]:
%matplotlib inline

In [22]:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import statsmodels.api as sm

import plotly
import cufflinks as cf
from plotly import graph_objs as go

import vizualizations

plotly.offline.init_notebook_mode()
cf.set_config_file(offline=True, theme='white')



In [5]:
!sbt "run-main ZeroIntelligenceApp"


[info] Loading global plugins from /Users/drpugh/.sbt/0.13/plugins
[info] Loading project definition from /Users/drpugh/Research/scalabm/models-library/gode-sunder/project
[info] Set current project to gode-sunder-model (in build file:/Users/drpugh/Research/scalabm/models-library/gode-sunder/)
[info] Running ZeroIntelligenceApp 
[info] -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
[success] Total time: 8 s, completed Apr 28, 2016 2:45:45 PM

Importing the raw data


In [6]:
# this is a bit of a hack as current log files are not valid JSON!
with open('./data/fills.log') as data_file:    
    zi_data = pd.read_json('[%s]' % ','.join(data_file.read().splitlines()))
    zi_data.set_index('timestamp', inplace=True)

In [7]:
zi_data.describe()


Out[7]:
askPrice bidPrice price quantity
count 107002.000000 107002.000000 107002.000000 107002.0
mean 78.095802 121.894666 99.954833 1.0
std 45.027579 45.101861 30.965784 0.0
min 1.000000 30.000000 2.000000 1.0
25% 39.000000 83.000000 75.000000 1.0
50% 78.000000 122.000000 100.000000 1.0
75% 117.000000 161.000000 125.000000 1.0
max 158.000000 199.000000 158.000000 1.0

Compute the bid-ask spread...


In [8]:
zi_data['spread'] = zi_data.bidPrice - zi_data.askPrice

In [9]:
zi_data.spread.describe()


Out[9]:
count    107002.000000
mean         43.798864
std          29.853797
min           1.000000
25%          20.000000
50%          39.000000
75%          62.000000
max         176.000000
Name: spread, dtype: float64

Compute raw returns

$$ r_{t+k} = \frac{p_{t+k} - p_{t}}{p_{t}} = \frac{\Delta p_{t+k}}{p_{t}}$$

Note that raw returns are bounded below! Bounds on price (at least in these simulations!) are $1 \le p_t \le \overline{p}$. Therefore lower bound on raw returns is...

$$ \underline{r} = \frac{1 - \overline{p}}{\overline{p}} \approx -1. $$

Similarly, then upper bound on raw returns is...

$$ \underline{r} = \frac{\overline{p} - 1}{1} \approx \overline{p}. $$

In [10]:
zi_data['raw_returns'] = zi_data.price.pct_change(periods=1)

In [11]:
zi_data.raw_returns.describe()


Out[11]:
count    107001.000000
mean          0.107884
std           0.529796
min          -0.830189
25%          -0.240602
50%           0.000000
75%           0.308824
max           5.888889
Name: raw_returns, dtype: float64

In [12]:
zi_data.raw_returns.iplot(xTitle='Time', yTitle='Return', title='Raw returns with unconstrained ZIA',
                          dimensions=(800, 500))


Compute logarithmic returns

Starting from raw returns, $r_{t+k}$ we see that...

$$ 1 + r_{t+k} = 1 + \frac{p_{t+k} - p_t}{p_t} = \frac{p_{t+k}}{p_t} $$

...taking logs yields...

$$ \ln \big(1 + r_{t+k}\big) = \ln p_{t+k} - \ln p_t = \Delta \ln p_{t+k} $$

...since $\ln \big(1 + r_{t+k}\big) \approx r_{t+k}$ for $r << 1$ we should expect that raw and logarithmic return formulas should yields similar results for "small" returns.


In [13]:
zi_data['log_returns'] = np.log(1 + zi_data.raw_returns)

Price impact


In [14]:
zi_data['mid_price'] = 0.5 * (zi_data.askPrice + zi_data.bidPrice)

How to best measure effective size? Particularly of ask orders!


In [15]:
zi_data['effective_size'] = zi_data.quantity

incoming_ask_orders = zi_data.bidPrice == zi_data.price
zi_data.loc[incoming_ask_orders, 'effective_size'] *= -zi_data.loc[incoming_ask_orders, 'askPrice']

incoming_bid_orders = zi_data.askPrice == zi_data.price
zi_data.loc[incoming_bid_orders, 'effective_size'] *= zi_data.loc[incoming_bid_orders, 'bidPrice']

Compute the measure of price impact: $\Delta \ln$ mid_price...


In [16]:
zi_data['price_impact'] = np.log(zi_data.mid_price).diff()

In [17]:
zi_data[incoming_ask_orders].head()


Out[17]:
askPrice bidPrice class price quantity tradable spread raw_returns log_returns mid_price effective_size price_impact
timestamp
2016-04-28 13:45:39.386 40 117 markets.Fill 117 1 Gpi2 77 0.017391 0.017242 78.5 -40 -0.399075
2016-04-28 13:45:39.387 72 108 markets.Fill 108 1 Gpi2 36 -0.076923 -0.080043 90.0 -72 0.136711
2016-04-28 13:45:39.389 46 102 markets.Fill 102 1 Gpi2 56 -0.209302 -0.234840 74.0 -46 -0.612260
2016-04-28 13:45:39.390 56 73 markets.Fill 73 1 Gpi2 17 -0.087500 -0.091567 64.5 -56 -0.556288
2016-04-28 13:45:39.391 61 65 markets.Fill 65 1 Gpi2 4 -0.350000 -0.430783 63.0 -61 -0.656780

In [20]:
zi_data.iplot(kind='scatter', x='effective_size', y='price_impact', mode='markers', size=3,
              xTitle='Effective Size', yTitle='Price Impact', title='Price impact with unconstrained ZIA',
              dimensions=(800, 500))



In [19]:
grouped_data = zi_data.groupby("effective_size")
grouped_data.price_impact.mean().iplot(kind='line', xTitle='Effective Size', yTitle='Price Impact',
                                       title='Price impact with unconstrained ZIA', dimensions=(800, 500))



In [23]:
# incoming bid orders
fig, ax = plt.subplots(1, 1, figsize=(10, 8))
grouped_data = zi_data.groupby("effective_size")
grouped_data.price_impact.mean().plot(color='r', alpha=0.75, ax=ax)
ax.scatter(zi_data.effective_size, zi_data.price_impact, edgecolor='b', s=1, alpha=0.05)
ax.set_ylabel("price_impact")
plt.show()


Cumulative distributions


In [25]:
fig = vizualizations.ecdf_plot(zi_data.log_returns.abs(), upper=True, figure_title="Zero Intelligence Agents",
                               xaxis_type='log', xaxis_title='Absolute Logarithmic Returns', yaxis_type='log')
plotly.offline.iplot(fig)



In [26]:
fig = vizualizations.ecdf_plot(zi_data.raw_returns.abs(), upper=True, figure_title="Zero Intelligence Agents",
                               xaxis_type='log', xaxis_title='Absolute Raw Returns', yaxis_type='log')
plotly.offline.iplot(fig)


Zero Intelligence Constrained

Endow traders with exogenous valuations and impose the requirement that traders never offer to buy (sell) a tradable at a price that is higher (lower) than their respective valuations of the tradable. Traders still choose their limit prices randomly, so in this sense traders still have "zero intelligence," its just that the support of the distribution from which these random limit prices are drawn is truncated to reflect their valuations.


In [27]:
!sbt "run-main ZeroIntelligenceConstrainedApp"


[info] Loading global plugins from /Users/drpugh/.sbt/0.13/plugins
[info] Loading project definition from /Users/drpugh/Research/scalabm/models-library/gode-sunder/project
[info] Set current project to gode-sunder-model (in build file:/Users/drpugh/Research/scalabm/models-library/gode-sunder/)
[info] Running ZeroIntelligenceConstrainedApp 
[info] -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
[success] Total time: 7 s, completed Apr 28, 2016 2:54:50 PM

In [28]:
# this is a bit of a hack as current log files are not valid JSON!
with open('./data/fills.log') as data_file:    
    zic_data = pd.read_json('[%s]' % ','.join(data_file.read().splitlines()))
    zic_data.set_index('timestamp', inplace=True)

In [29]:
zic_data.describe()


Out[29]:
askPrice bidPrice price quantity
count 35127.000000 35127.000000 35127.000000 35127.0
mean 87.827540 117.374982 103.099866 1.0
std 29.239148 27.870561 14.762798 0.0
min 0.000000 76.000000 76.000000 1.0
25% 68.000000 94.000000 91.000000 1.0
50% 93.000000 113.000000 103.000000 1.0
75% 112.000000 137.000000 115.000000 1.0
max 150.000000 198.000000 192.000000 1.0

In [30]:
zic_data['spread'] = zic_data.bidPrice - zic_data.askPrice
zic_data['raw_returns'] = zic_data.price.pct_change(periods=1)
zic_data['log_returns'] = np.log(1 + zic_data.raw_returns)

In [31]:
zic_data.price.iplot(xTitle='Time', yrange=[0, 200], yTitle='Asset Price',
                     title='Asset prices with constrained ZIA')



In [32]:
zic_data.spread.iplot(xTitle='Time', yrange=[0, 200], yTitle='Bid-Ask Spread',
                      title='Spread with constrained ZIA')



In [33]:
zic_data.log_returns.iplot(xTitle='Time', yTitle='Return', title='Logarithmic returns with unconstrained ZIA')


Imposing budget constraints on traders reduces spreads...


In [34]:
zi_data.spread.describe()


Out[34]:
count    107002.000000
mean         43.798864
std          29.853797
min           1.000000
25%          20.000000
50%          39.000000
75%          62.000000
max         176.000000
Name: spread, dtype: float64

In [35]:
zic_data.spread.describe()


Out[35]:
count    35127.000000
mean        29.547442
std         21.685910
min          1.000000
25%         12.000000
50%         25.000000
75%         44.000000
max        177.000000
Name: spread, dtype: float64

In [ ]:
# render the plot locally in the notebook
fig = vizualizations.kde_plot([zi_data.spread, zic_data.spread], figure_title="Zero Intelligence Agents",
                              xaxis_title='Bid-Ask Spread', xaxis_range=[1, 200])
plotly.offline.iplot(fig)

Imposing budget constraints on traders reduces variance in returns...


In [43]:
zi_data.log_returns.describe()


Out[43]:
count    99744.000000
mean         0.000027
std          0.451085
min         -1.474000
25%         -0.272415
50%          0.000000
75%          0.269523
max          2.224624
Name: log_returns, dtype: float64

In [44]:
zic_data.log_returns.describe()


Out[44]:
count    32289.000000
mean        -0.000007
std          0.182676
min         -0.746257
25%         -0.106295
50%          0.000000
75%          0.105361
max          0.571786
Name: log_returns, dtype: float64

In [307]:
# render the plot locally in the notebook
fig = kde_plot([zi_data.log_returns, zic_data.log_returns], figure_title="Zero Intelligence Agents",
               xaxis_title='Logarithmic Returns')
plotly.offline.iplot(fig)



In [ ]: