In [16]:
from alephnull import TradingAlgorithm
from alephnull.roll_method import roll
from alephnull.sources.futures_data_frame_source import FuturesDataFrameSource
%load_ext autoreload
%autoreload 2
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload

In [17]:
import pandas as pd
from pandas import DataFrame, Series
import datetime as dt
import numpy as np
import string

Create some synthetic Futures Contracts

In [370]:
num_syms = 3
num_expirations = 3
expirations = 'F,G,H,J,K,M,N,Q,U,V,X,Z'.split(',')
fields = 'price, volume, open_interest'.split(', ')
syms = [''.join(np.random.choice(list(string.uppercase), np.random.randint(2,3))) 
        for x in xrange(num_syms)]
contracts = [(sym , np.random.choice(expirations) + str(np.random.randint(14,15)))
             for x in xrange(num_expirations) for sym in syms]
cols = list(set([contract + (field,) for field in fields for contract in contracts]))
rands = np.random.standard_normal(size=(500, len(cols)))
now = dt.datetime.utcnow()
dates = pd.date_range(end= now, start= now - dt.timedelta(days = rands.shape[0]-1))
df_data = DataFrame(rands, index=dates, columns = pd.MultiIndex.from_tuples(cols)
                    ).swapaxes(0,1).sort_index().swapaxes(0,1)
base = (100, 3200, 50)
ratios = [x + 1. / base[0] for x in base]
for col in cols:
    if 'price' in col:
        df_data[col].ix[0] = abs(df_data[col].ix[0] * base[0])
        df_data[col].ix[1:] = abs(df_data[col].cumsum())
    if 'volume' in col:
        df_data[col].ix[0] = abs(df_data[col].ix[0] * base[1])
        df_data[col].ix[1:] = df_data[col].cumsum()
        df_data[col] = df_data[col].map(lambda x: int(x))
    if 'open_interest' in col:
        df_data[col].ix[0] = abs(df_data[col].ix[0] * base[2])
        df_data[col].ix[1:] = df_data[col].cumsum()
        df_data[col] = df_data[col].map(lambda x: int(x))
df_data = df_data.tz_localize('UTC')

Plot the Open Interest as a reference to ensure roll method is working below

In [372]:
[df_data.xs('open_interest', level=2, axis=1)[sym].plot(figsize(18,6)) for sym in syms]
Out[372]:
[<matplotlib.axes.AxesSubplot at 0x110593c10>,
 <matplotlib.axes.AxesSubplot at 0x1111a9110>,
 <matplotlib.axes.AxesSubplot at 0x1105d7650>]

Run a simple algo that buys 5 contracts. If successful we will have 5 contracts of each underlying in the front month throughout the backtest

In [374]:
class Bot(TradingAlgorithm):
    def initialize(self, *args):
        self.invested = False
    
    @roll(lambda x: x[x['open_interest'] == x['open_interest'].max()])
    def handle_data(self, data):
        if not self.invested:
            for sym in data.keys():
                self.order((sym, data[sym]['contract']), 5)
        self.invested = True
            
fut_data = FuturesDataFrameSource(df_data)        
instance = Bot()
stats = instance.run(fut_data)
[2014-03-12 16:34] INFO: Performance: Simulated 340 trading days out of 340.
[2014-03-12 16:34] INFO: Performance: first open: 2012-10-31 13:31:00+00:00
[2014-03-12 16:34] INFO: Performance: last close: 2014-03-10 20:00:00+00:00

Positions at the end of the run seem to indicate that everything worked. Zero position amounts are from Back Months

In [375]:
positions = instance.perf_tracker.get_portfolio().positions
DataFrame({k:v.__dict__ for k,v in positions.iteritems()})
Out[375]:
(DI, G14) (DI, M14) (DI, Z14) (FA, Q14) (WA, F14)
amount 0 5 0 5 5
contract G14 M14 Z14 Q14 F14
cost_basis 0 60.75256 0 209.4928 22.01358
last_sale_price 37.01913 72.3499 23.19549 199.5955 8.732255
sid DI DI DI FA WA

5 rows × 5 columns

Validate that nothing strange happened by glossing through the position history and cross checking with the plots above

In [376]:
frames = {}
for x,y in stats.positions.map(lambda x: 
           DataFrame([Series(pos, name=pos['sid']) 
           for pos in x])).iteritems():
    frames[x] = y.stack()
positions = pd.concat(frames, axis=1).T.drop('sid', level=1, axis=1)
positions
Out[376]:
DI FA WA
amount contract cost_basis last_sale_price amount contract cost_basis last_sale_price amount contract cost_basis last_sale_price
2012-10-31 20:00:00 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2012-11-01 20:00:00 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2012-11-02 20:00:00 5 M14 50.80433 50.73663 5 Q14 209.4928 209.4607 5 F14 22.01358 21.98357
2012-11-05 21:00:00 5 M14 50.80433 51.71685 5 Q14 209.4928 211.0899 5 F14 22.01358 22.21932
2012-11-06 21:00:00 5 M14 50.80433 52.76928 5 Q14 209.4928 210.919 5 F14 22.01358 23.25214
2012-11-07 21:00:00 5 M14 50.80433 51.67932 5 Q14 209.4928 210.2529 5 F14 22.01358 23.12201
2012-11-08 21:00:00 5 M14 50.80433 51.84999 5 Q14 209.4928 210.4453 5 F14 22.01358 21.57932
2012-11-09 21:00:00 5 M14 50.80433 51.6363 5 Q14 209.4928 211.6427 5 F14 22.01358 22.62798
2012-11-12 21:00:00 5 M14 50.80433 50.78293 5 Q14 209.4928 212.6477 5 F14 22.01358 24.1957
2012-11-13 21:00:00 5 M14 50.80433 51.18998 5 Q14 209.4928 213.8781 5 F14 22.01358 25.34143
2012-11-14 21:00:00 5 M14 50.80433 51.70225 5 Q14 209.4928 212.8006 5 F14 22.01358 26.34794
2012-11-15 21:00:00 5 M14 50.80433 51.4033 5 Q14 209.4928 210.827 5 F14 22.01358 25.01337
2012-11-16 21:00:00 5 M14 50.80433 50.92872 5 Q14 209.4928 209.9294 5 F14 22.01358 24.30254
2012-11-19 21:00:00 5 M14 50.80433 50.22163 5 Q14 209.4928 209.6542 5 F14 22.01358 24.84704
2012-11-20 21:00:00 5 M14 50.80433 49.8276 5 Q14 209.4928 208.5715 5 F14 22.01358 25.53828
2012-11-21 21:00:00 5 M14 50.80433 49.98367 5 Q14 209.4928 210.1676 5 F14 22.01358 24.62599
2012-11-23 18:00:00 5 M14 50.80433 50.50054 5 Q14 209.4928 209.7182 5 F14 22.01358 23.27757
2012-11-26 21:00:00 5 Z14 28.48885 26.0171 5 Q14 209.4928 209.3812 5 F14 22.01358 26.52091
2012-11-27 21:00:00 5 Z14 28.48885 26.08029 5 Q14 209.4928 209.3661 5 F14 22.01358 27.64932
2012-11-28 21:00:00 5 Z14 28.48885 26.83381 5 Q14 209.4928 210.434 5 F14 22.01358 27.25256
2012-11-29 21:00:00 5 Z14 28.48885 28.51329 5 Q14 209.4928 210.9695 5 F14 22.01358 27.53519
2012-11-30 21:00:00 5 Z14 28.48885 27.25144 5 Q14 209.4928 211.5205 5 F14 22.01358 27.64399
2012-12-03 21:00:00 5 Z14 28.48885 25.90268 5 Q14 209.4928 211.1015 5 F14 22.01358 26.82798
2012-12-04 21:00:00 5 Z14 28.48885 26.38956 5 Q14 209.4928 210.8249 5 F14 22.01358 27.14176
2012-12-05 21:00:00 5 Z14 28.48885 25.81062 5 Q14 209.4928 209.3508 5 F14 22.01358 25.90122
2012-12-06 21:00:00 5 Z14 28.48885 25.01555 5 Q14 209.4928 210.0686 5 F14 22.01358 26.12648
2012-12-07 21:00:00 5 Z14 28.48885 24.77428 5 Q14 209.4928 209.9914 5 F14 22.01358 25.5987
2012-12-10 21:00:00 5 Z14 28.48885 24.69644 5 Q14 209.4928 207.6808 5 F14 22.01358 27.40317
2012-12-11 21:00:00 5 Z14 28.48885 23.96699 5 Q14 209.4928 209.1239 5 F14 22.01358 27.91811
2012-12-12 21:00:00 5 Z14 28.48885 26.3201 5 Q14 209.4928 207.5379 5 F14 22.01358 27.08905
2012-12-13 21:00:00 5 Z14 28.48885 26.26296 5 Q14 209.4928 205.4829 5 F14 22.01358 27.71243
2012-12-14 21:00:00 5 Z14 28.48885 25.73226 5 Q14 209.4928 203.7544 5 F14 22.01358 26.62378
2012-12-17 21:00:00 5 Z14 28.48885 21.62282 5 Q14 209.4928 204.1507 5 F14 22.01358 23.89703
2012-12-18 21:00:00 5 Z14 28.48885 21.35401 5 Q14 209.4928 205.5076 5 F14 22.01358 24.94289
2012-12-19 21:00:00 5 Z14 28.48885 22.1612 5 Q14 209.4928 204.9643 5 F14 22.01358 24.58232
2012-12-20 21:00:00 5 Z14 28.48885 21.69704 5 Q14 209.4928 205.6274 5 F14 22.01358 24.3077
2012-12-21 21:00:00 5 Z14 28.48885 21.35365 5 Q14 209.4928 204.1212 5 F14 22.01358 24.72192
2012-12-24 18:00:00 5 Z14 28.48885 20.56179 5 Q14 209.4928 201.845 5 F14 22.01358 22.31264
2012-12-26 21:00:00 5 M14 57.30668 57.22168 5 Q14 209.4928 200.691 5 F14 22.01358 23.87183
2012-12-27 21:00:00 5 Z14 22.66385 22.63384 5 Q14 209.4928 199.2407 5 F14 22.01358 23.48448
2012-12-28 21:00:00 5 Z14 22.66385 23.11799 5 Q14 209.4928 198.4602 5 F14 22.01358 23.37776
2012-12-31 21:00:00 5 Z14 27.17123 27.14123 5 Q14 209.4928 199.9301 5 F14 22.01358 20.25868
2013-01-02 21:00:00 5 M14 59.08434 58.99535 5 Q14 209.4928 202.3973 5 F14 22.01358 19.76907
2013-01-03 21:00:00 5 Z14 26.42045 26.39044 5 Q14 209.4928 202.5536 5 F14 22.01358 18.26771
2013-01-04 21:00:00 5 M14 58.10909 58.01868 5 Q14 209.4928 202.6067 5 F14 22.01358 17.76623
2013-01-07 21:00:00 5 M14 59.69878 58.39937 5 Q14 209.4928 200.6688 5 F14 22.01358 17.50945
2013-01-08 21:00:00 5 Z14 29.17177 29.14177 5 Q14 209.4928 201.3372 5 F14 22.01358 18.14174
2013-01-09 21:00:00 5 Z14 29.17177 31.01085 5 Q14 209.4928 200.3549 5 F14 22.01358 18.06462
2013-01-10 21:00:00 5 M14 58.45543 58.35648 5 Q14 209.4928 200.2338 5 F14 22.01358 17.23888
2013-01-11 21:00:00 5 M14 58.45543 57.4198 5 Q14 209.4928 199.5381 5 F14 22.01358 17.25377
2013-01-14 21:00:00 5 M14 58.14036 58.04737 5 Q14 209.4928 198.9998 5 F14 22.01358 17.03985
2013-01-15 21:00:00 5 M14 58.14036 57.16919 5 Q14 209.4928 199.6341 5 F14 22.01358 16.36429
2013-01-16 21:00:00 5 M14 58.14036 57.42338 5 Q14 209.4928 197.8063 5 F14 22.01358 15.57308
2013-01-17 21:00:00 5 M14 58.14036 56.25681 5 Q14 209.4928 198.0364 5 F14 22.01358 16.63198
2013-01-18 21:00:00 5 Z14 35.76161 35.73161 5 Q14 209.4928 197.6888 5 F14 22.01358 15.99576
2013-01-22 21:00:00 5 Z14 35.76161 33.04457 5 Q14 209.4928 196.5653 5 F14 22.01358 17.67235
2013-01-23 21:00:00 5 Z14 35.76161 33.33457 5 Q14 209.4928 196.7235 5 F14 22.01358 18.95663
2013-01-24 21:00:00 5 Z14 35.76161 35.29553 5 Q14 209.4928 196.6849 5 F14 22.01358 17.2861
2013-01-25 21:00:00 5 Z14 35.76161 35.53924 5 Q14 209.4928 196.6474 5 F14 22.01358 17.1595
2013-01-28 21:00:00 5 Z14 35.76161 36.62099 5 Q14 209.4928 196.6105 5 F14 22.01358 18.96598
... ... ... ... ... ... ... ... ... ... ... ...

340 rows × 12 columns

No gaps or flatlnes in the equity curve are also validating

In [377]:
stats.portfolio_value.plot()
Out[377]:
<matplotlib.axes.AxesSubplot at 0x1088eef90>

Currently Roll Returns a dict vs BarData, this needs patched

In [473]:
from pandas import Timestamp
from alephnull.protocol import SIDData, BarData

bar = {
       'FA': {'type': 4, 'price': 210.06995717015968, 'contract': 'Q14', 'datetime': 
              Timestamp('2012-10-31 16:33:30.959157', tz=None), 'volume': 508, 'sid': 'FA',
              'source_id': 'FuturesDataFrameSource-aa7290ce07234a3a81d6ce626f4c9e58', 
              'dt': Timestamp('2012-10-31 16:33:30.959157', tz=None), 'open_interest': 52}, 
       
       'WA': {'type': 4, 'price': 23.765399420437273, 'contract': 'F14', 'datetime': 
              Timestamp('2012-10-31 16:33:30.959157', tz=None), 'volume': 2427, 'sid': 'WA',
              'source_id': 'FuturesDataFrameSource-aa7290ce07234a3a81d6ce626f4c9e58', 
              'dt': Timestamp('2012-10-31 16:33:30.959157', tz=None), 'open_interest': 60}, 
       
       'DI': {'type': 4, 'price': 50.47594609660444, 'contract': 'M14', 'datetime': 
              Timestamp('2012-10-31 16:33:30.959157', tz=None), 'volume': 57, 'sid': 'DI', 
              'source_id': 'FuturesDataFrameSource-aa7290ce07234a3a81d6ce626f4c9e58', 'dt': 
              Timestamp('2012-10-31 16:33:30.959157', tz=None), 'open_interest': 49}
       }

# {k:SIDData(v) for k,v in bar.iteritems()}
current_data = BarData()
current_data.__dict__['_data'].update({k:SIDData(v) for k,v in bar.iteritems()})
current_data.__dict__
Out[473]:
{'_contains_override': None,
 '_data': {'DI': SIDData({'volume': 57, 'sid': 'DI', 'source_id': 'FuturesDataFrameSource-aa7290ce07234a3a81d6ce626f4c9e58', 'open_interest': 49, 'price': 50.47594609660444, 'type': 4, 'dt': Timestamp('2012-10-31 16:33:30.959157', tz=None), 'contract': 'M14', 'datetime': Timestamp('2012-10-31 16:33:30.959157', tz=None)}),
  'FA': SIDData({'volume': 508, 'sid': 'FA', 'source_id': 'FuturesDataFrameSource-aa7290ce07234a3a81d6ce626f4c9e58', 'open_interest': 52, 'price': 210.06995717015968, 'type': 4, 'dt': Timestamp('2012-10-31 16:33:30.959157', tz=None), 'contract': 'Q14', 'datetime': Timestamp('2012-10-31 16:33:30.959157', tz=None)}),
  'WA': SIDData({'volume': 2427, 'sid': 'WA', 'source_id': 'FuturesDataFrameSource-aa7290ce07234a3a81d6ce626f4c9e58', 'open_interest': 60, 'price': 23.765399420437273, 'type': 4, 'dt': Timestamp('2012-10-31 16:33:30.959157', tz=None), 'contract': 'F14', 'datetime': Timestamp('2012-10-31 16:33:30.959157', tz=None)})}}

Now that the above has been implemented in Roll

In [474]:
class BarData(TradingAlgorithm):
    def initialize(self, *args):
        self.counter = 0

    @roll(lambda x: x[x['open_interest'] == x['open_interest'].max()])
    def handle_data(self, data):
        if self.counter < 10:
            print data
        self.counter +=1
        

futdata = FuturesDataFrameSource(df_data)
instance = BarData()
stats = instance.run(futdata)
<alephnull.protocol.BarData object at 0x110ea8610>
<alephnull.protocol.BarData object at 0x110ea8d90>
<alephnull.protocol.BarData object at 0x110ea8f90>
<alephnull.protocol.BarData object at 0x114007590>
<alephnull.protocol.BarData object at 0x110ea8cd0>
<alephnull.protocol.BarData object at 0x110ea8390>
<alephnull.protocol.BarData object at 0x110ea8450>
<alephnull.protocol.BarData object at 0x110ea8e10>
<alephnull.protocol.BarData object at 0x110ea8f50>
<alephnull.protocol.BarData object at 0x110ea8690>
[2014-03-12 17:31] INFO: Performance: Simulated 340 trading days out of 340.
[2014-03-12 17:31] INFO: Performance: first open: 2012-10-31 13:31:00+00:00
[2014-03-12 17:31] INFO: Performance: last close: 2014-03-10 20:00:00+00:00



Roll now returns a BarData Object!

In [477]:
class FixedRollAlgo(TradingAlgorithm):
    @roll(lambda x: x[x['open_interest'] == x['open_interest'].max()])
    def handle_data(self, data):
        if np.random.randint(0,100) > 70:
            for sym in data.keys():
                # Now that this is fixed we can do . indexing
                #eg (data[sym].contract)
                self.order((sym, data[sym].contract), 
                           1 * np.random.choice([-1.0, 1.0]))
            
futdata = FuturesDataFrameSource(df_data)
instance = FixedRollAlgo()
stats = instance.run(futdata)
[2014-03-12 17:41] INFO: Performance: Simulated 340 trading days out of 340.
[2014-03-12 17:41] INFO: Performance: first open: 2012-10-31 13:31:00+00:00
[2014-03-12 17:41] INFO: Performance: last close: 2014-03-10 20:00:00+00:00

Double Check Before Declaring Fixed

In [479]:
stats.portfolio_value.plot()
Out[479]:
<matplotlib.axes.AxesSubplot at 0x110e89a10>

Equity Curve looks accurate

In [482]:
frames = {}
for x,y in stats.positions.map(lambda x: 
           DataFrame([Series(pos, name=pos['sid']) 
           for pos in x])).iteritems():
    frames[x] = y.stack()
positions = pd.concat(frames, axis=1).T.drop('sid', level=1, axis=1)
positions
Out[482]:
DI FA WA
amount contract cost_basis last_sale_price amount contract cost_basis last_sale_price amount contract cost_basis last_sale_price
2012-10-31 20:00:00 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2012-11-01 20:00:00 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2012-11-02 20:00:00 -1 M14 50.70512 50.73663 1 Q14 209.4908 209.4607 -1 F14 21.95357 21.98357
2012-11-05 21:00:00 NaN NaN NaN NaN 2 Q14 210.3054 211.0899 -2 F14 22.07145 22.21932
2012-11-06 21:00:00 1 M14 52.8008 52.76928 1 Q14 209.7219 210.919 -3 F14 22.45501 23.25214
2012-11-07 21:00:00 1 M14 52.8008 51.67932 1 Q14 209.7219 210.2529 -3 F14 22.45501 23.12201
2012-11-08 21:00:00 2 M14 52.34119 51.84999 2 Q14 210.0986 210.4453 -2 F14 22.87785 21.57932
2012-11-09 21:00:00 2 M14 52.34119 51.6363 2 Q14 210.0986 211.6427 -2 F14 22.87785 22.62798
2012-11-12 21:00:00 2 M14 52.02124 50.78293 NaN NaN NaN NaN -2 F14 22.03918 24.1957
2012-11-13 21:00:00 2 M14 52.02124 51.18998 NaN NaN NaN NaN -2 F14 22.03918 25.34143
2012-11-14 21:00:00 3 M14 51.92552 51.70225 1 Q14 212.8307 212.8006 -3 F14 23.46543 26.34794
2012-11-15 21:00:00 2 M14 52.20255 51.4033 2 Q14 211.8438 210.827 -4 F14 23.84491 25.01337
2012-11-16 21:00:00 2 M14 52.20255 50.92872 2 Q14 211.8438 209.9294 -4 F14 23.84491 24.30254
2012-11-19 21:00:00 1 M14 53.66309 50.22163 3 Q14 210.7542 209.6542 -3 F14 23.58309 24.84704
2012-11-20 21:00:00 NaN NaN NaN NaN 4 Q14 210.2161 208.5715 -4 F14 24.06439 25.53828
2012-11-21 21:00:00 NaN NaN NaN NaN 4 Q14 210.2161 210.1676 -4 F14 24.06439 24.62599
2012-11-23 18:00:00 NaN NaN NaN NaN 4 Q14 210.2161 209.7182 -4 F14 24.06439 23.27757
2012-11-26 21:00:00 -1 Z14 28.42885 26.0171 5 Q14 210.1974 209.3812 -5 F14 24.10515 26.52091
2012-11-27 21:00:00 -1 Z14 28.42885 26.08029 5 Q14 210.1974 209.3661 -5 F14 24.10515 27.64932
2012-11-28 21:00:00 NaN NaN NaN NaN 6 Q14 210.2418 210.434 -6 F14 24.62471 27.25256
2012-11-29 21:00:00 NaN NaN NaN NaN 6 Q14 210.2418 210.9695 -6 F14 24.62471 27.53519
2012-11-30 21:00:00 NaN NaN NaN NaN 6 Q14 210.2418 211.5205 -6 F14 24.62471 27.64399
2012-12-03 21:00:00 -1 Z14 27.53289 25.90268 5 Q14 210.0361 211.1015 -7 F14 25.00581 26.82798
2012-12-04 21:00:00 -1 Z14 27.53289 26.38956 5 Q14 210.0361 210.8249 -7 F14 25.00581 27.14176
2012-12-05 21:00:00 -1 Z14 27.53289 25.81062 5 Q14 210.0361 209.3508 -7 F14 25.00581 25.90122
2012-12-06 21:00:00 -1 Z14 27.53289 25.01555 5 Q14 210.0361 210.0686 -7 F14 25.00581 26.12648
2012-12-07 21:00:00 -1 Z14 27.53289 24.77428 5 Q14 210.0361 209.9914 -7 F14 25.00581 25.5987
2012-12-10 21:00:00 -2 Z14 25.91499 24.69644 4 Q14 210.195 207.6808 -8 F14 25.1101 27.40317
2012-12-11 21:00:00 -1 Z14 27.833 23.96699 5 Q14 209.9868 209.1239 -7 F14 24.70467 27.91811
2012-12-12 21:00:00 -1 Z14 27.833 26.3201 5 Q14 209.9868 207.5379 -7 F14 24.70467 27.08905
2012-12-13 21:00:00 -1 Z14 27.833 26.26296 5 Q14 209.9868 205.4829 -7 F14 24.70467 27.71243
2012-12-14 21:00:00 -1 Z14 27.833 25.73226 5 Q14 209.9868 203.7544 -7 F14 24.70467 26.62378
2012-12-17 21:00:00 -2 Z14 25.99987 21.62282 6 Q14 208.9843 204.1507 -6 F14 24.45768 23.89703
2012-12-18 21:00:00 -2 Z14 25.99987 21.35401 6 Q14 208.9843 205.5076 -6 F14 24.45768 24.94289
2012-12-19 21:00:00 -3 Z14 24.71031 22.1612 5 Q14 209.7943 204.9643 -7 F14 24.4712 24.58232
2012-12-20 21:00:00 -3 Z14 24.71031 21.69704 5 Q14 209.7943 205.6274 -7 F14 24.4712 24.3077
2012-12-21 21:00:00 -2 Z14 26.37365 21.35365 6 Q14 208.8538 204.1212 -8 F14 24.49879 24.72192
2012-12-24 18:00:00 -2 Z14 26.37365 20.56179 6 Q14 208.8538 201.845 -8 F14 24.49879 22.31264
2012-12-26 21:00:00 -3 M14 57.17921 57.22168 5 Q14 210.4923 200.691 -7 F14 24.58407 23.87183
2012-12-27 21:00:00 -4 Z14 22.60384 22.63384 6 Q14 208.6221 199.2407 -6 F14 24.76233 23.48448
2012-12-28 21:00:00 -4 Z14 22.60384 23.11799 6 Q14 208.6221 198.4602 -6 F14 24.76233 23.37776
2012-12-31 21:00:00 -2 Z14 27.11123 27.14123 8 Q14 206.0726 199.9301 -4 F14 26.63322 20.25868
2013-01-02 21:00:00 -1 M14 58.96299 58.99535 9 Q14 205.4432 202.3973 -3 F14 28.81601 19.76907
2013-01-03 21:00:00 -1 Z14 26.36044 26.39044 9 Q14 205.4432 202.5536 -3 F14 28.81601 18.26771
2013-01-04 21:00:00 -1 M14 57.98626 58.01868 9 Q14 205.4432 202.6067 -3 F14 28.81601 17.76623
2013-01-07 21:00:00 NaN NaN NaN NaN 10 Q14 204.9688 200.6688 -4 F14 25.98187 17.50945
2013-01-08 21:00:00 NaN NaN NaN NaN 10 Q14 204.9688 201.3372 -4 F14 25.98187 18.14174
2013-01-09 21:00:00 NaN NaN NaN NaN 10 Q14 204.9688 200.3549 -4 F14 25.98187 18.06462
2013-01-10 21:00:00 NaN NaN NaN NaN 10 Q14 204.9688 200.2338 -4 F14 25.98187 17.23888
2013-01-11 21:00:00 NaN NaN NaN NaN 10 Q14 204.9688 199.5381 -4 F14 25.98187 17.25377
2013-01-14 21:00:00 1 M14 58.07989 58.04737 9 Q14 205.6354 198.9998 -5 F14 24.18746 17.03985
2013-01-15 21:00:00 1 M14 58.07989 57.16919 9 Q14 205.6354 199.6341 -5 F14 24.18746 16.36429
2013-01-16 21:00:00 1 M14 58.07989 57.42338 9 Q14 205.6354 197.8063 -5 F14 24.18746 15.57308
2013-01-17 21:00:00 NaN NaN NaN NaN 10 Q14 204.8785 198.0364 -6 F14 22.92322 16.63198
2013-01-18 21:00:00 NaN NaN NaN NaN 10 Q14 204.8785 197.6888 -6 F14 22.92322 15.99576
2013-01-22 21:00:00 NaN NaN NaN NaN 10 Q14 204.8785 196.5653 -6 F14 22.92322 17.67235
2013-01-23 21:00:00 NaN NaN NaN NaN 10 Q14 204.8785 196.7235 -6 F14 22.92322 18.95663
2013-01-24 21:00:00 1 Z14 35.32553 35.29553 11 Q14 204.1363 196.6849 -7 F14 22.11363 17.2861
2013-01-25 21:00:00 2 Z14 35.44738 35.53924 10 Q14 204.8882 196.6474 -6 F14 22.93432 17.1595
2013-01-28 21:00:00 3 Z14 35.84859 36.62099 9 Q14 205.8113 196.6105 -7 F14 22.36312 18.96598
... ... ... ... ... ... ... ... ... ... ... ...

340 rows × 12 columns

aside from the NaN's (Not sure if bug or if null positions result in NaNs), positions seem to be persistent through roll over