├── README.md ├── output ├── Strat-CorrelRptr.txt ├── Strat-LmtOrdrs Backtest.txt └── Strat-Paris Backtest.txt └── src ├── Backtester ├── Backtester.py ├── StubOrderEvent.py ├── StubTickEvent.py └── __init__.py ├── StockOrder.py ├── StockPosition.py ├── StockTradable.py ├── Strat-CorrelRptr.py ├── Strat-Empty.py ├── Strat-LmtOrdrs.py ├── Strat-Pairs.py ├── ibDataTypes.py ├── ibHFT.py ├── ibUtil.py └── ticks 10 mins - Jun 25 2014.csv /README.md: -------------------------------------------------------------------------------- 1 | Readme 2 | ================================ 3 | 4 | This is an enhancement to the current version of High Frequency Trading Model with IB @ https://github.com/jamesmawm/High-Frequency-Trading-Model-with-IB. 5 | 6 | In this version, I've decoupled modules, used functional programming styles, made things simpler and enable switching between different strategies. Oh, and a custom backtester too with bid/ask price simulated events (Zipline gave my code cancer). 7 | 8 | Again, these files are for evaluation purposes only and do not constitute real profitable trading models. 9 | 10 | Features 11 | =============== 12 | - Reuse multiple strategies on the same IB framework. See src/Strat-Empty.py for a template. 13 | - src/Backtester/* contains custom backtester which reads in a CSV file. Supports limit orders-based strategies with bid ask price simulation. 14 | 15 | Strategies 16 | ============= 17 | - Strat-Pairs.py: Pairs trading through cointegration, using OLS and Pandas. 18 | - Strat-LmtOrdrs.py: Limit-order based strategy with GUI dashboard. Works with backtester. 19 | - Strat-CorrelRptr.py: Stores ticks in a dataframe and reports the correlations. 20 | 21 | How To 22 | ============= 23 | - Connecting to IB and getting live ticks in 3 simple steps: 24 | ``` 25 | self.ibhft = ibHFT.IbHFT() 26 | self.ibhft.set_connection_with_api_gateway(False) 27 | self.ibhft.start_data_stream(self.on_started 28 | , self.on_tick 29 | , STOCKS_TO_STREAM 30 | , self.on_position_changed) 31 | 32 | ``` 33 | 34 | - Same 3 simple steps in getting historical data: 35 | ``` 36 | self.ibhft = ibHFT.IbHFT() 37 | self.ibhft.set_connection_with_api_gateway(False) 38 | self.ibhft.start_historical_data_stream(self.stocks_to_stream 39 | , self.duration 40 | , self.interval 41 | , self.process_historical_data) 42 | ``` 43 | 44 | - For backtesting, same 3 simple steps: 45 | ``` 46 | self.ibhft = bt.Backtester() 47 | self.ibhft.set_csv_file("ticks 10 mins - Jun 25 2014.csv") 48 | self.ibhft.start_data_stream(self.on_started 49 | , self.on_tick 50 | , STOCKS_TO_STREAM 51 | , self.on_position_changed) 52 | ``` 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /output/Strat-CorrelRptr.txt: -------------------------------------------------------------------------------- 1 | Server Version: 70 2 | TWS Time at connection:20140703 23:38:31 CST 3 | Market data farm connection is OK:ibdemo 4 | Unhandled errcode: 5 | HMDS data farm connection is OK:demohmds 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | JPM USB C WFC BAC MS 247 | JPM 1.000000 -0.692976 -0.260477 0.856929 -0.141720 0.450907 248 | USB -0.692976 1.000000 0.199568 -0.799777 0.122546 -0.116489 249 | C -0.260477 0.199568 1.000000 -0.378609 0.075669 0.212819 250 | WFC 0.856929 -0.799777 -0.378609 1.000000 -0.210411 0.213091 251 | BAC -0.141720 0.122546 0.075669 -0.210411 1.000000 -0.384212 252 | MS 0.450907 -0.116489 0.212819 0.213091 -0.384212 1.000000 253 | 254 | [6 rows x 6 columns] 255 | 256 | Process finished with exit code 0 257 | -------------------------------------------------------------------------------- /output/Strat-LmtOrdrs Backtest.txt: -------------------------------------------------------------------------------- 1 | Cycle completed. 2 | Back results: 3 | 20140625 20:10:59 C 42.86 S 100 -100 4285.0 4 | 20140625 20:11:08 C 42.89 B 100 0 -5.0 5 | 20140625 20:11:28 C 42.9 S 100 -100 4284.0 6 | 20140625 20:11:28 C 42.93 B 100 0 -10.0 7 | 20140625 20:11:56 C 42.9 B 100 100 -4301.0 8 | 20140625 20:11:56 C 42.85 S 100 0 -17.0 -------------------------------------------------------------------------------- /output/Strat-Paris Backtest.txt: -------------------------------------------------------------------------------- 1 | Back results: 2 | 20140625 20:10:55 C 42.85 B 100 100 -4286.0 3 | 20140625 20:10:55 MS 32.05 S 100 -100 -1082.0 4 | 20140625 20:13:56 C 43.08 S 100 0 3225.0 5 | 20140625 20:13:56 C 43.1 S 100 -100 7534.0 6 | 20140625 20:13:56 MS 32.1 B 100 0 4323.0 7 | 20140625 20:13:56 C 43.12 B 100 0 10.0 8 | 20140625 20:14:22 C 43.12 B 100 100 -4303.0 9 | 20140625 20:14:22 MS 32.1 S 100 -100 -1094.0 10 | 20140625 20:14:28 C 43.08 S 100 0 3213.0 11 | 20140625 20:14:28 C 43.08 S 100 -100 7520.0 12 | 20140625 20:14:28 MS 32.1 B 100 0 4309.0 13 | 20140625 20:14:28 C 43.1 B 100 0 -2.0 14 | 20140625 20:14:53 C 43.1 B 100 100 -4313.0 15 | 20140625 20:14:53 MS 32.08 S 100 -100 -1106.0 16 | 20140625 20:16:09 C 43.11 S 100 0 3204.0 17 | 20140625 20:16:09 C 43.11 S 100 -100 7514.0 18 | 20140625 20:16:09 MS 32.1 B 100 0 4303.0 19 | 20140625 20:16:09 C 43.13 B 100 0 -11.0 20 | 20140625 20:16:22 C 43.1 B 100 100 -4322.0 21 | 20140625 20:16:22 MS 32.08 S 100 -100 -1115.0 22 | 20140625 20:20:34 C 43.12 S 100 0 3196.0 23 | 20140625 20:20:34 C 43.12 S 100 -100 7507.0 24 | 20140625 20:20:34 MS 32.1 B 100 0 4296.0 25 | 20140625 20:20:34 C 43.14 B 100 0 -19.0 26 | 27 | Process finished with exit code 0 28 | -------------------------------------------------------------------------------- /src/Backtester/Backtester.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | from ibHFT import IbHFT 7 | from StubTickEvent import StubTickEvent as TickEvent 8 | from StubOrderEvent import StubOrderEvent as OrderEvent 9 | import csv 10 | import pandas as pd 11 | import ibDataTypes 12 | 13 | 14 | class Backtester(IbHFT): 15 | 16 | def __init__(self): 17 | IbHFT.__init__(self) 18 | self.COST_PER_SHARE = 0.005 19 | self.on_tick_func = None 20 | self.on_position_changed_func = None 21 | self.csv_file = None 22 | 23 | def set_csv_file(self, filename): 24 | self.csv_file = filename 25 | 26 | def start_data_stream(self, on_started_func, on_tick, stock_codes, on_pos_changed=None): 27 | dataframe = pd.read_csv(self.csv_file) 28 | csv_stock_codes = [stock_code for stock_code in dataframe][1:] 29 | 30 | self.init_context(csv_stock_codes) 31 | self.assign_functions(on_pos_changed, on_tick) 32 | 33 | for row in range(dataframe.shape[0]): 34 | self.on_data_row(dataframe, row, stock_codes) 35 | 36 | self.report_backtest_results() 37 | 38 | def report_backtest_results(self): 39 | print "Back results:" 40 | 41 | position = {k: 0 for k in self.stock_codes} 42 | pnl = 0 43 | for order in self.orders_filled: 44 | 45 | qty = order.qty 46 | stock_code = order.stock_code 47 | comm = self.get_commission(qty) 48 | is_buy = order.is_buy 49 | price = order.price 50 | mkt_value = qty * price 51 | position[stock_code] += qty * (1 if is_buy else -1) 52 | pnl += mkt_value * (1 if not is_buy else -1) - comm 53 | 54 | print order.filled_time, stock_code, price, "B" if order.is_buy else "S", qty, position[stock_code], pnl 55 | 56 | def get_commission(self, qty): 57 | return max(1, self.COST_PER_SHARE*qty) 58 | 59 | def on_data_row(self, dataframe, row, stock_codes): 60 | current_time = None 61 | for i, header in enumerate(dataframe): 62 | if i == 0: 63 | current_time = dataframe[header][row] 64 | else: 65 | ticker_id = i-1 66 | stock_code = header 67 | 68 | ''' TODO: retrive previous last price and dont' send tick event if same 69 | ''' 70 | 71 | tick_event = TickEvent() 72 | tick_event.tickerId = ticker_id 73 | tick_event.price = dataframe[stock_code][row] 74 | tick_event.typeName = ibDataTypes.MSG_TYPE_TICK_PRICE 75 | tick_event.field = ibDataTypes.FIELD_LAST_PRICE 76 | self.send_event_tick(tick_event, current_time) 77 | 78 | bid_event = TickEvent() 79 | bid_event.tickerId = ticker_id 80 | bid_event.price = dataframe[stock_code][row] - 0.01 81 | bid_event.typeName = ibDataTypes.MSG_TYPE_TICK_PRICE 82 | bid_event.field = ibDataTypes.FIELD_BID_PRICE 83 | self.send_event_tick(bid_event, current_time) 84 | 85 | ask_event = TickEvent() 86 | ask_event.tickerId = ticker_id 87 | ask_event.price = dataframe[stock_code][row] + 0.01 88 | ask_event.typeName = ibDataTypes.MSG_TYPE_TICK_PRICE 89 | ask_event.field = ibDataTypes.FIELD_ASK_PRICE 90 | self.send_event_tick(ask_event, current_time) 91 | 92 | def send_event_tick(self, msg, current_time): 93 | self.tick_event(msg) 94 | self.match_orders(current_time) 95 | 96 | def match_orders(self, timestamp): 97 | 98 | for pending_order in self.orders_pending: 99 | stock_code = pending_order.stock_code 100 | current_bid_px = self.get_current_bid_price(stock_code) 101 | current_ask_px = self.get_current_ask_price(stock_code) 102 | order_px = pending_order.price 103 | is_buy = pending_order.is_buy 104 | order_id = pending_order.order_id 105 | 106 | if (is_buy and order_px >= current_ask_px) \ 107 | or (not is_buy and order_px <= current_bid_px): 108 | 109 | pending_order.filled_time = timestamp 110 | self.execute_order(order_id) 111 | 112 | def execute_order(self, order_id): 113 | order_event = OrderEvent() 114 | order_event.orderId = order_id 115 | order_event.typeName = ibDataTypes.MSG_TYPE_ORDER_STATUS 116 | order_event.status = ibDataTypes.ORDER_STATUS_FILLED 117 | order_event.remaining = 0 118 | self.logger(order_event) 119 | 120 | @staticmethod 121 | def read_csv_file(filepath): 122 | with open(filepath, "rb") as fileobject: 123 | return csv.reader(fileobject) 124 | return None 125 | 126 | #Override 127 | def replace_order(self, order): 128 | return 129 | 130 | #Override 131 | def cancel_order(self, order): 132 | return 133 | 134 | #Override 135 | def place_order(self, this_order_id, tradable, stock_order): 136 | return -------------------------------------------------------------------------------- /src/Backtester/StubOrderEvent.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | 7 | class StubOrderEvent: 8 | 9 | def __init__(self): 10 | self.typeName = None 11 | self.status = None 12 | self.orderId = 0 13 | self.remaining = 0 14 | return -------------------------------------------------------------------------------- /src/Backtester/StubTickEvent.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | 7 | class StubTickEvent: 8 | 9 | def __init__(self): 10 | self.tickerId = 0 11 | self.typeName = None 12 | self.price = 0 13 | self.field = None 14 | return 15 | 16 | -------------------------------------------------------------------------------- /src/Backtester/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesmawm/IB-Trading-Models-And-Backtester/28461d4628fb5e4aef9e44e9d4eb6565b1501255/src/Backtester/__init__.py -------------------------------------------------------------------------------- /src/StockOrder.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | from ibUtil import * 7 | 8 | 9 | class StockOrder: 10 | def __init__(self, order_id, stock_code, is_buy, qty, price=None, stop_price = None): 11 | self.order_id = order_id 12 | self.stock_code = stock_code 13 | self.is_buy = is_buy 14 | self.qty = qty 15 | self.price = price 16 | self.stop_price = stop_price 17 | self.filled_timed = None 18 | 19 | def get_stock_order(self): 20 | return create_stock_order(self.order_id, self.qty, self.is_buy, self.price, self.stop_price) 21 | 22 | def get_order_position(self): 23 | return self.qty * (1 if self.is_buy else -1) 24 | -------------------------------------------------------------------------------- /src/StockPosition.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | from ibUtil import * 7 | 8 | 9 | class StockPosition: 10 | 11 | def __init__(self, stock_code): 12 | self.stock_code = stock_code 13 | self.position = 0 14 | self.market_value = 0 15 | self.average_price = 0 16 | self.realized_pnl = 0 17 | self.unrealized_pnl = 0 18 | self.tradable = create_stock_contract(self.stock_code) -------------------------------------------------------------------------------- /src/StockTradable.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | class StockTick: 7 | def __init__(self): 8 | self.bid_price = 0 9 | self.ask_price = 0 10 | self.last_price = 0 11 | self.last_volume = 0 12 | self.volume = 0 13 | self.bid_volume = 0 14 | self.ask_volume = 0 -------------------------------------------------------------------------------- /src/Strat-CorrelRptr.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | """ Strategy: Correlation Reporter 7 | 8 | Prints the correlations using Pandas. 9 | """ 10 | 11 | import pandas as pd 12 | import ibDataTypes as DataType 13 | import ibHFT 14 | 15 | 16 | class TradingStrategy: 17 | 18 | def __init__(self): 19 | self.ibhft = None 20 | self.pd_last_prices = pd.DataFrame() 21 | self.stocks_to_stream = ["JPM", "USB", "C", "WFC", "BAC", "MS"] 22 | self.duration = DataType.DURATION_1_DAY 23 | self.interval = DataType.BAR_SIZE_1_MIN 24 | return 25 | 26 | def process_historical_data(self, msg): 27 | print msg 28 | 29 | vwap = msg.WAP 30 | stock_index = msg.reqId 31 | if vwap != -1: 32 | stock_code = self.stocks_to_stream[stock_index] 33 | date_time = msg.date 34 | #open = msg.open 35 | #high = msg.high 36 | close = msg.close 37 | #volume = msg.volume 38 | 39 | self.pd_last_prices = self.pd_last_prices.set_value(date_time, stock_code, close) 40 | 41 | elif vwap == -1: 42 | if len(self.pd_last_prices.columns) == len(self.stocks_to_stream): 43 | self.on_completion() 44 | 45 | def on_completion(self): 46 | self.ibhft.set_is_shutdown() 47 | print self.pd_last_prices.corr() 48 | 49 | def run(self): 50 | self.ibhft = ibHFT.IbHFT() 51 | self.ibhft.set_connection_with_api_gateway(False) 52 | self.ibhft.start_historical_data_stream(self.stocks_to_stream 53 | , self.duration 54 | , self.interval 55 | , self.process_historical_data) 56 | 57 | if __name__ == '__main__': 58 | TradingStrategy().run() -------------------------------------------------------------------------------- /src/Strat-Empty.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | """ Template Strategy 7 | 8 | An empty template to create strategies. 9 | """ 10 | 11 | import Backtester.Backtester as bt 12 | import ibHFT 13 | 14 | # Changeable params 15 | STOCKS_TO_STREAM = ["PEP", "KO"] 16 | 17 | 18 | class TradingStrategy: 19 | 20 | def __init__(self): 21 | self.ibhft = None 22 | return 23 | 24 | def on_position_changed(self): 25 | print "on position changed." 26 | 27 | def on_tick(self, ticks, stock_code, field_type): 28 | print "tick" 29 | 30 | def on_started(self): 31 | return 32 | 33 | def run(self): 34 | self.ibhft = ibHFT.IbHFT() 35 | self.ibhft.set_connection_with_api_gateway(False) 36 | self.ibhft.start_data_stream(self.on_started 37 | , self.on_tick 38 | , STOCKS_TO_STREAM 39 | , self.on_position_changed) 40 | 41 | def run_backtest(self): 42 | self.ibhft = bt.Backtester() 43 | self.ibhft.set_csv_file("ticks 10 mins - Jun 25 2014.csv") 44 | self.ibhft.start_data_stream(self.on_started 45 | , self.on_tick 46 | , STOCKS_TO_STREAM 47 | , self.on_position_changed) 48 | 49 | if __name__ == '__main__': 50 | # TradingStrategy().run() 51 | TradingStrategy().run_backtest() -------------------------------------------------------------------------------- /src/Strat-LmtOrdrs.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | """ Strategy: Limit Orders 7 | 8 | Place limit orders just above the bid ask prices. 9 | Close position when exceed certain thresholds. 10 | """ 11 | 12 | import Backtester.Backtester as bt 13 | from Tkinter import * 14 | import ibHFT 15 | 16 | # Fixed params 17 | TICK_SIZE = 0.01 18 | 19 | # Changeable params 20 | STOCKS_TO_STREAM = ["C", "JPM"] 21 | CLCYLE_LIMIT = 3 22 | TRADE_QTY = 100 23 | LMT_ORDER_SIZE = 0.01 24 | SPREAD_TICKS_TO_TRADE = 0.01 25 | TAKE_PROFIT_TICKS = TICK_SIZE * 1 26 | MAX_TICKS_LOSS = 0.03 27 | 28 | 29 | class TradingStrategy: 30 | 31 | # Strategy states 32 | STATE_PLACE_ORDERS = 0 33 | STATE_POSITION_OPENED = 1 34 | STATE_TERMINATED = 2 35 | 36 | def __init__(self): 37 | self.ibhft = None 38 | self.current_state = self.STATE_PLACE_ORDERS 39 | self.current_ticks = None 40 | self.sell_px = self.buy_px = self.working_ask_px = self.working_bid_px = 0 41 | self.tx_px = self.limit_px = self.loss_px = 0 42 | self.current_cycle = 0 43 | self.current_sampling_thread = None 44 | self.tk = Tk() 45 | self.label_stock = StringVar() 46 | self.label_bidask = StringVar() 47 | self.label_last = StringVar() 48 | self.label_ticks = StringVar() 49 | self.label_position = StringVar() 50 | self.label_orders = StringVar() 51 | self.label_pnl = StringVar() 52 | self.label_cycle = StringVar() 53 | self.label_traded = StringVar() 54 | return 55 | 56 | def on_tick(self, ticks, stock_code, field_type): 57 | self.current_ticks = ticks 58 | 59 | stock_a = STOCKS_TO_STREAM[0] 60 | is_position_flat = self.ibhft.is_position_flat(stock_a) 61 | if self.is_no_open_orders() \ 62 | and is_position_flat \ 63 | and self.current_state == self.STATE_PLACE_ORDERS: 64 | 65 | self.place_limit_orders(ticks, stock_a) 66 | 67 | elif not is_position_flat: 68 | self.monitor_to_cover_position(stock_a) 69 | 70 | # Else, wait for trade execution. 71 | 72 | self.update_ui() 73 | 74 | def monitor_to_cover_position(self, stock_code): 75 | pos = self.ibhft.get_position(stock_code) 76 | if pos != 0: 77 | if self.current_state == self.STATE_PLACE_ORDERS: 78 | self.current_state = self.STATE_POSITION_OPENED 79 | self.send_take_profit_limit_order(stock_code, pos) 80 | 81 | elif self.current_state == self.STATE_POSITION_OPENED: 82 | self.update_covering_position(stock_code, pos) 83 | 84 | def update_covering_position(self, stock_code, pos): 85 | if pos == 0: 86 | return 87 | 88 | current_bid_px = self.ibhft.get_current_bid_price(stock_code) 89 | current_ask_px = self.ibhft.get_current_ask_price(stock_code) 90 | 91 | is_long = pos > 0 92 | self.tx_px = self.buy_px if is_long else self.sell_px 93 | market_px = current_bid_px if is_long else current_ask_px 94 | ticks_lost = max(0, self.tx_px - market_px) if is_long else max(0, market_px - self.tx_px) 95 | 96 | self.loss_px = self.tx_px - MAX_TICKS_LOSS if is_long else self.tx_px + MAX_TICKS_LOSS 97 | 98 | pendings = self.ibhft.get_number_of_pending_orders() 99 | if ticks_lost >= MAX_TICKS_LOSS: 100 | 101 | if pendings == 1: 102 | self.ibhft.place_limit_order(stock_code, not is_long, TRADE_QTY, market_px) 103 | 104 | stock_order = self.ibhft.get_pending_orders()[0] 105 | self.limit_px = stock_order.price 106 | 107 | elif pendings > 1: 108 | self.update_market_order(market_px, not is_long) 109 | 110 | else: 111 | 112 | if pendings == 1: 113 | stock_order = self.ibhft.get_pending_orders()[0] 114 | self.limit_px = stock_order.price 115 | 116 | if pendings > 1: 117 | self.remove_market_order() 118 | 119 | def remove_market_order(self): 120 | max_order_id = max([stock_order.order_id for stock_order in self.ibhft.get_pending_orders()]) 121 | self.ibhft.remove_pending_order(max_order_id) 122 | 123 | def update_market_order(self, new_px, is_buy): 124 | max_order_id = max([stock_order.order_id for stock_order in self.ibhft.get_pending_orders()]) 125 | for stock_order in self.ibhft.get_pending_orders(): 126 | if stock_order.order_id == max_order_id: 127 | if stock_order.price != new_px \ 128 | or stock_order.is_buy != is_buy: 129 | stock_order.price = new_px 130 | stock_order.is_buy = is_buy 131 | self.ibhft.replace_order(stock_order) 132 | return 133 | 134 | def send_take_profit_limit_order(self, stock_code, pos): 135 | 136 | if pos < 0: 137 | # Cover position - Go LONG at bid px. 138 | self.current_state = self.STATE_POSITION_OPENED 139 | take_profit_px = self.sell_px - TAKE_PROFIT_TICKS 140 | self.update_pending_order(stock_code, take_profit_px) 141 | 142 | elif pos > 0: 143 | # Cover position - Go SHORT at ask px. 144 | self.current_state = self.STATE_POSITION_OPENED 145 | take_profit_px = self.buy_px + TAKE_PROFIT_TICKS 146 | self.update_pending_order(stock_code, take_profit_px) 147 | 148 | def update_pending_order(self, stock_code, new_price): 149 | for order in self.ibhft.get_pending_orders(): 150 | if order.stock_code == stock_code: 151 | self.ibhft.update_order_with_price_and_type(order, new_price, None) 152 | return 153 | 154 | def on_position_changed(self): 155 | stock_a = STOCKS_TO_STREAM[0] 156 | 157 | if self.ibhft.is_position_flat(stock_a): 158 | self.ibhft.remove_all_pending_orders() 159 | 160 | if self.current_state == self.STATE_POSITION_OPENED: 161 | self.current_cycle += 1 162 | if self.current_cycle < CLCYLE_LIMIT: 163 | self.current_state = self.STATE_PLACE_ORDERS 164 | else: 165 | self.current_state = self.STATE_TERMINATED 166 | self.tk.quit() 167 | print "Cycle completed." 168 | 169 | def is_no_open_orders(self): 170 | num_open_orders = self.ibhft.get_number_of_pending_orders() 171 | return num_open_orders == 0 172 | 173 | def place_limit_orders(self, ticks, stock_code): 174 | a_bid_px = ticks[stock_code].bid_price 175 | a_ask_px = ticks[stock_code].ask_price 176 | 177 | if a_bid_px == 0 or a_ask_px == 0: 178 | return 179 | 180 | self.working_bid_px = a_bid_px 181 | self.working_ask_px = a_ask_px 182 | self.buy_px = self.working_bid_px - LMT_ORDER_SIZE 183 | self.sell_px = self.working_ask_px + LMT_ORDER_SIZE 184 | 185 | self.ibhft.place_limit_order(stock_code, True, TRADE_QTY, self.buy_px) 186 | self.ibhft.place_limit_order(stock_code, False, TRADE_QTY, self.sell_px) 187 | 188 | def update_ui(self): 189 | stock_a = STOCKS_TO_STREAM[0] 190 | self.label_stock.set(stock_a) 191 | 192 | bidaskstr = "({bidvol}) {bid}/{ask} ({askvol})".format( 193 | bidvol=self.current_ticks[stock_a].bid_volume 194 | , bid=self.current_ticks[stock_a].bid_price 195 | , ask=self.current_ticks[stock_a].ask_price 196 | , askvol=self.current_ticks[stock_a].ask_volume) 197 | self.label_bidask.set(bidaskstr) 198 | 199 | laststr = "{last} ({lastvol})".format( 200 | last=self.current_ticks[stock_a].last_price 201 | , lastvol=self.current_ticks[stock_a].last_volume) 202 | self.label_last.set(laststr) 203 | 204 | ordersstr = "{pending} / {working}".format( 205 | pending=self.ibhft.get_number_of_pending_orders() 206 | , working=self.ibhft.get_number_of_filled_orders()) 207 | self.label_orders.set(ordersstr) 208 | 209 | a_position = self.ibhft.get_stock_position(stock_a) 210 | pnlstr = "{unreal} / {real}".format( 211 | unreal=a_position.unrealized_pnl 212 | , real=a_position.realized_pnl) 213 | self.label_pnl.set(pnlstr) 214 | 215 | self.label_position.set(self.ibhft.get_position(stock_a)) 216 | 217 | self.label_ticks.set(self.ibhft.get_number_of_ticks()) 218 | 219 | cyclestr = "{cyc} / {cycmax}".format( 220 | cyc=self.current_cycle 221 | , cycmax=CLCYLE_LIMIT) 222 | self.label_cycle.set(cyclestr) 223 | 224 | tradedstr = "{traded} ({limit}/{loss})".format( 225 | traded=self.tx_px 226 | , limit=self.limit_px 227 | , loss=self.loss_px) 228 | self.label_traded.set(tradedstr) 229 | 230 | def create_ui(self): 231 | 232 | def create_row(header, label, row): 233 | Label(self.tk, text=header).grid(row=row,column=0) 234 | Label(self.tk, textvariable=label).grid(row=row,column=1) 235 | row += 1 236 | return row 237 | 238 | row_index = 0 239 | row_index = create_row("Stock:", self.label_stock, row_index) 240 | row_index = create_row("Bid/Ask:", self.label_bidask, row_index) 241 | row_index = create_row("Last:", self.label_last, row_index) 242 | row_index = create_row("Traded:", self.label_traded, row_index) 243 | row_index = create_row("Unreal/Real:", self.label_pnl, row_index) 244 | row_index = create_row("Pending/Filled", self.label_orders, row_index) 245 | row_index = create_row("Position:", self.label_position, row_index) 246 | row_index = create_row("Ticks:", self.label_ticks, row_index) 247 | row_index = create_row("Cycle:", self.label_cycle, row_index) 248 | 249 | def on_started(self): 250 | self.create_ui() 251 | self.tk.mainloop() 252 | 253 | if self.current_sampling_thread is not None: 254 | self.current_sampling_thread.cancel() 255 | 256 | def run(self): 257 | self.ibhft = ibHFT.IbHFT() 258 | self.ibhft.set_connection_with_api_gateway(False) 259 | self.ibhft.start_data_stream(self.on_started 260 | , self.on_tick 261 | , STOCKS_TO_STREAM 262 | , self.on_position_changed) 263 | 264 | def run_backtest(self): 265 | self.ibhft = bt.Backtester() 266 | self.ibhft.set_csv_file("ticks 10 mins - Jun 25 2014.csv") 267 | self.ibhft.start_data_stream(self.on_started 268 | , self.on_tick 269 | , STOCKS_TO_STREAM 270 | , self.on_position_changed) 271 | 272 | if __name__ == '__main__': 273 | TradingStrategy().run() 274 | # TradingStrategy().run_backtest() -------------------------------------------------------------------------------- /src/Strat-Pairs.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | """ Strategy: Pairs trading through cointegration 7 | 8 | Uses OLS to determine order of co-integration every self.window_length. 9 | Pair-trading by mean-reversion. 10 | """ 11 | 12 | import Backtester.Backtester as bt 13 | import pandas as pd 14 | from pandas.stats.api import ols 15 | from Tkinter import * 16 | import ibHFT 17 | import threading 18 | import ibDataTypes 19 | import time 20 | import numpy as np 21 | 22 | # Fixed params 23 | TICK_SIZE = 0.01 24 | 25 | # Changeable params 26 | STOCKS_TO_STREAM = ["C", "MS", "BAC"] 27 | CLCYLE_LIMIT = 3 28 | TRADE_QTY = 100 29 | LMT_ORDER_SIZE = 0.01 30 | SPREAD_TICKS_TO_TRADE = 0.01 31 | TAKE_PROFIT_TICKS = TICK_SIZE * 1 32 | MAX_TICKS_LOSS = 0.03 33 | 34 | 35 | class TradingStrategy: 36 | 37 | # Strategy states 38 | STATE_PLACE_ORDERS = 0 39 | STATE_POSITION_OPENED = 1 40 | STATE_TERMINATED = 2 41 | 42 | def __init__(self): 43 | self.ibhft = None 44 | self.current_state = self.STATE_PLACE_ORDERS 45 | self.current_ticks = None 46 | self.sell_px = self.buy_px = self.working_ask_px = self.working_bid_px = 0 47 | self.tx_px = self.limit_px = self.loss_px = 0 48 | self.current_cycle = 0 49 | self.current_sampling_thread = None 50 | self.pd_last_prices = pd.DataFrame() 51 | self.tk = Tk() 52 | self.label_stock = StringVar() 53 | self.label_bidask = StringVar() 54 | self.label_last = StringVar() 55 | self.label_ticks = StringVar() 56 | self.label_position = StringVar() 57 | self.label_orders = StringVar() 58 | self.label_pnl = StringVar() 59 | self.label_zscore = StringVar() 60 | self.label_traded = StringVar() 61 | self.spreads = [] 62 | self.window_length = 30 63 | self.is_bootstrapped = False 64 | return 65 | 66 | def sample_ticks_at_interval(self): 67 | 68 | if self.current_state is not self.STATE_TERMINATED: 69 | self.current_sampling_thread = threading.Timer(1.0, self.sample_ticks_at_interval).start() 70 | 71 | if self.current_ticks is None: 72 | return 73 | 74 | store_time = time.strftime(ibDataTypes.DATE_TIME_FORMAT) 75 | 76 | for stock_code, stock_tick in self.current_ticks.iteritems(): 77 | self.pd_last_prices = self.pd_last_prices.set_value(store_time 78 | , stock_code 79 | , stock_tick.last_price) 80 | 81 | rows = self.pd_last_prices.shape[0] 82 | if rows > self.window_length: 83 | self.is_bootstrapped = True 84 | self.pd_last_prices = self.pd_last_prices[-self.window_length:] 85 | 86 | def on_tick(self, ticks, stock_code, field_type): 87 | self.current_ticks = ticks 88 | 89 | if not self.is_bootstrapped: 90 | print "Bootstrapping...." 91 | return 92 | 93 | [stock_code_a, stock_code_b, stock_code_c] = STOCKS_TO_STREAM 94 | 95 | # Assign prices of interest 96 | get_last_prices = lambda code: self.pd_last_prices[code] 97 | prices_a = get_last_prices(stock_code_a) 98 | prices_b = get_last_prices(stock_code_b) 99 | 100 | a_last_px, a_bid_px, a_ask_px = self.get_current_prices(stock_code_a) 101 | b_last_px, b_bid_px, b_ask_px = self.get_current_prices(stock_code_b) 102 | 103 | if a_bid_px == 0 or a_ask_px == 0 or b_bid_px == 0 or b_ask_px == 0: 104 | return 105 | 106 | # Do OLS for coeffs 107 | slope1, intercept1, mean1, stdev1 = self.get_coeffs_from_ols(prices_a, prices_b) 108 | spread_lasta_lastb = self.calculate_spread(a_last_px, b_last_px, slope1, intercept1) 109 | zscore = self.get_zscore(spread_lasta_lastb, mean1, stdev1) 110 | 111 | is_have_pending_orders = (self.ibhft.get_number_of_pending_orders() > 0) 112 | a_pos = self.ibhft.get_position(stock_code_a) 113 | b_pos = self.ibhft.get_position(stock_code_b) 114 | is_position_flat = a_pos == 0 and b_pos == 0 115 | 116 | if zscore >= 2.0 and not is_have_pending_orders and is_position_flat: 117 | self.ibhft.place_limit_order(stock_code_a, False, TRADE_QTY, a_bid_px) 118 | self.ibhft.place_limit_order(stock_code_b, True, TRADE_QTY, b_ask_px) 119 | 120 | elif zscore <= -2.0 and not is_have_pending_orders and is_position_flat: 121 | self.ibhft.place_limit_order(stock_code_a, True, TRADE_QTY, a_ask_px) 122 | self.ibhft.place_limit_order(stock_code_b, False, TRADE_QTY, b_bid_px) 123 | 124 | elif abs(zscore) < .5 and not is_have_pending_orders and not is_position_flat: 125 | (is_buy_a, a_px) = (True, a_ask_px) if a_pos < 0 else (False, a_bid_px) 126 | (is_buy_b, b_px) = (True, b_ask_px) if b_pos < 0 else (False, b_bid_px) 127 | self.ibhft.place_limit_order(stock_code_a, is_buy_a, TRADE_QTY, a_px) 128 | self.ibhft.place_limit_order(stock_code_b, is_buy_b, TRADE_QTY, b_px) 129 | 130 | elif is_have_pending_orders: 131 | 132 | is_mkt_went_away_on_both_sides = True 133 | for order in self.ibhft.get_pending_orders(): 134 | code = order.stock_code 135 | last_px, bid_px, ask_px = self.get_current_prices(code) 136 | mkt_px = ask_px if order.is_buy else bid_px 137 | ticks_diff = abs(mkt_px - order.price) 138 | 139 | if ticks_diff <= 0.02: 140 | is_mkt_went_away_on_both_sides = False 141 | 142 | if is_mkt_went_away_on_both_sides: 143 | self.ibhft.remove_all_pending_orders() 144 | 145 | self.update_ui(zscore) 146 | 147 | def get_coeffs_from_ols(self, a, b): 148 | slope, intercept = ols(y=a, x=b).beta[['x', 'intercept']] 149 | spreads_pair_ab = self.calculate_spread(a, b, slope, intercept) 150 | mean, stdev = self.get_mean_and_std(spreads_pair_ab) 151 | return slope, intercept, mean, stdev 152 | 153 | @staticmethod 154 | def get_mean_and_std(values): 155 | return np.mean(values), np.std(values) 156 | 157 | @staticmethod 158 | def calculate_spread(a, b, slope, intercept): 159 | return a - (b * slope + intercept) 160 | 161 | @staticmethod 162 | def get_zscore(a, mean, stdev): 163 | return 0 if stdev==0 else (a-mean)/stdev 164 | 165 | def get_current_prices(self, code): 166 | last_px = self.ibhft.get_current_last_price(code) 167 | bid_px = self.ibhft.get_current_bid_price(code) 168 | ask_px = self.ibhft.get_current_ask_price(code) 169 | return last_px, bid_px, ask_px 170 | 171 | def flush_sampled_data_to_csv(self, filename): 172 | self.pd_last_prices.to_csv(filename, sep=',', encoding='utf-8') 173 | 174 | def on_position_changed(self): 175 | stock_a = STOCKS_TO_STREAM[0] 176 | 177 | if self.ibhft.is_position_flat(stock_a): 178 | self.ibhft.remove_all_pending_orders() 179 | 180 | if self.current_state == self.STATE_POSITION_OPENED: 181 | self.current_cycle += 1 182 | if self.current_cycle < CLCYLE_LIMIT: 183 | self.current_state = self.STATE_PLACE_ORDERS 184 | else: 185 | self.current_state = self.STATE_TERMINATED 186 | self.tk.quit() 187 | print "Cycle completed." 188 | 189 | def is_no_open_orders(self): 190 | num_open_orders = self.ibhft.get_number_of_pending_orders() 191 | return num_open_orders == 0 192 | 193 | def place_limit_orders(self, ticks, stock_code): 194 | a_bid_px = ticks[stock_code].bid_price 195 | a_ask_px = ticks[stock_code].ask_price 196 | 197 | if a_bid_px == 0 or a_ask_px == 0: 198 | return 199 | 200 | self.working_bid_px = a_bid_px 201 | self.working_ask_px = a_ask_px 202 | self.buy_px = self.working_bid_px - LMT_ORDER_SIZE 203 | self.sell_px = self.working_ask_px + LMT_ORDER_SIZE 204 | 205 | self.ibhft.place_limit_order(stock_code, True, TRADE_QTY, self.buy_px) 206 | self.ibhft.place_limit_order(stock_code, False, TRADE_QTY, self.sell_px) 207 | 208 | def update_ui(self, zscore): 209 | stock_a = STOCKS_TO_STREAM[0] 210 | self.label_stock.set(stock_a) 211 | 212 | bidaskstr = "({bidvol}) {bid}/{ask} ({askvol})".format( 213 | bidvol=self.current_ticks[stock_a].bid_volume 214 | , bid=self.current_ticks[stock_a].bid_price 215 | , ask=self.current_ticks[stock_a].ask_price 216 | , askvol=self.current_ticks[stock_a].ask_volume) 217 | self.label_bidask.set(bidaskstr) 218 | 219 | laststr = "{last} ({lastvol})".format( 220 | last=self.current_ticks[stock_a].last_price 221 | , lastvol=self.current_ticks[stock_a].last_volume) 222 | self.label_last.set(laststr) 223 | 224 | ordersstr = "{pending} / {working}".format( 225 | pending=self.ibhft.get_number_of_pending_orders() 226 | , working=self.ibhft.get_number_of_filled_orders()) 227 | self.label_orders.set(ordersstr) 228 | 229 | a_position = self.ibhft.get_stock_position(stock_a) 230 | pnlstr = "{unreal} / {real}".format( 231 | unreal=a_position.unrealized_pnl 232 | , real=a_position.realized_pnl) 233 | self.label_pnl.set(pnlstr) 234 | 235 | self.label_position.set(self.ibhft.get_position(stock_a)) 236 | self.label_ticks.set(self.ibhft.get_number_of_ticks()) 237 | self.label_zscore.set("{0:.3f}".format(zscore)) 238 | 239 | tradedstr = "{traded} ({limit}/{loss})".format( 240 | traded=self.tx_px 241 | , limit=self.limit_px 242 | , loss=self.loss_px) 243 | self.label_traded.set(tradedstr) 244 | 245 | def create_ui(self): 246 | 247 | def create_row(header, label, row): 248 | Label(self.tk, text=header).grid(row=row,column=0) 249 | Label(self.tk, textvariable=label).grid(row=row,column=1) 250 | row += 1 251 | return row 252 | 253 | row_index = 0 254 | row_index = create_row("Stock:", self.label_stock, row_index) 255 | row_index = create_row("Bid/Ask:", self.label_bidask, row_index) 256 | row_index = create_row("Last:", self.label_last, row_index) 257 | row_index = create_row("Traded:", self.label_traded, row_index) 258 | row_index = create_row("Unreal/Real:", self.label_pnl, row_index) 259 | row_index = create_row("Pending/Filled", self.label_orders, row_index) 260 | row_index = create_row("Position:", self.label_position, row_index) 261 | row_index = create_row("Ticks:", self.label_ticks, row_index) 262 | row_index = create_row("Z-score:", self.label_zscore, row_index) 263 | 264 | def on_started(self): 265 | self.sample_ticks_at_interval() 266 | self.create_ui() 267 | self.tk.mainloop() 268 | 269 | if self.current_sampling_thread is not None: 270 | self.current_sampling_thread.cancel() 271 | 272 | def run(self): 273 | self.ibhft = ibHFT.IbHFT() 274 | self.ibhft.set_connection_with_api_gateway(False) 275 | self.ibhft.start_data_stream(self.on_started 276 | , self.on_tick 277 | , STOCKS_TO_STREAM 278 | , self.on_position_changed) 279 | 280 | def run_backtest(self): 281 | def setup_bootstrap_conditions(): 282 | self.pd_last_prices = pd.read_csv("ticks 10 mins - Jun 25 2014.csv") 283 | self.pd_last_prices = self.pd_last_prices[-self.window_length:] 284 | self.is_bootstrapped = True 285 | 286 | setup_bootstrap_conditions() 287 | self.ibhft = bt.Backtester() 288 | self.ibhft.set_csv_file("ticks 10 mins - Jun 25 2014.csv") 289 | self.ibhft.start_data_stream(self.on_started 290 | , self.on_tick 291 | , STOCKS_TO_STREAM 292 | , self.on_position_changed) 293 | 294 | if __name__ == '__main__': 295 | # TradingStrategy().run() 296 | TradingStrategy().run_backtest() -------------------------------------------------------------------------------- /src/ibDataTypes.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | # https://www.interactivebrokers.com/en/software/api/apiguide/java/reqhistoricaldata.htm 7 | # https://www.interactivebrokers.com/en/software/api/apiguide/tables/tick_types.htm 8 | 9 | FIELD_BID_SIZE = 0 10 | FIELD_BID_PRICE = 1 11 | FIELD_ASK_PRICE = 2 12 | FIELD_ASK_SIZE = 3 13 | FIELD_LAST_PRICE = 4 14 | FIELD_LAST_SIZE = 5 15 | FIELD_HIGH = 6 16 | FIELD_LOW = 7 17 | FIELD_VOLUME = 8 18 | FIELD_CLOSE_PRICE = 9 19 | FIELD_AVG_VOLUME = 21 20 | FIELD_BID_EXCH = 32 21 | FIELD_ASK_EXCH = 33 22 | FIELD_AUCTION_VOLUME = 34 23 | FIELD_AUCTION_PRICE = 35 24 | FIELD_LAST_TIMESTAMP = 45 25 | FIELD_HALTED = 49 26 | FIELD_TRADE_COUNT = 54 27 | FIELD_TRADE_RATE = 55 28 | FIELD_VOLUME_RATE = 56 29 | 30 | FIELD_HALTED_NOT_HALTED = 0 31 | FIELD_HALTED_IS_HALTED = 1 32 | FIELD_HALTED_BY_VOLATILITY = 2 33 | 34 | DURATION_1_HR = "3600 S" 35 | DURATION_1_MIN = "60 S" 36 | DURATION_1_DAY = "1 D" 37 | 38 | BAR_SIZE_1_SEC = "1 secs" 39 | BAR_SIZE_1_MIN = "1 min" 40 | 41 | RTH_ALL = 0 42 | RTH_ONLY_TRADING_HRS = 1 43 | 44 | WHAT_TO_SHOW_TRADES = "TRADES" 45 | WHAT_TO_SHOW_MID_PT = "MIDPOINT" 46 | WHAT_TO_SHOW_BID = "BID" 47 | WHAT_TO_SHOW_ASK = "ASK" 48 | WHAT_TO_SHOW_BID_ASK = "BID_ASK" 49 | WHAT_TO_SHOW_HVOL = "HISTORICAL_VOLATILITY" 50 | WHAT_TO_SHOW_OPT_IMPV = "OPTION_IMPLIED_VOLATILITY" 51 | 52 | DATEFORMAT_STRING = 1 53 | DATEFORMAT_UNIX_TS = 2 54 | 55 | MSG_TYPE_HISTORICAL_DATA = "historicalData" 56 | MSG_TYPE_UPDATE_PORTFOLIO = "updatePortfolio" 57 | MSG_TYPE_ACCOUNT_UPDATE = "updateAccountValue" 58 | MSG_TYPE_MANAGED_ACCOUNTS = "managedAccounts" 59 | MSG_TYPE_NEXT_ORDER_ID = "nextValidId" 60 | MSG_TYPE_TICK_PRICE = "tickPrice" 61 | MSG_TYPE_TICK_STRING = "tickString" 62 | MSG_TYPE_STICK_SIZE = "tickSize" 63 | MSG_TYPE_ORDER_STATUS = "orderStatus" 64 | MSG_TYPE_ERROR = "error" 65 | 66 | ORDER_STATUS_SUBMITTED = "Submitted" 67 | ORDER_STATUS_FILLED = "Filled" 68 | 69 | DATE_TIME_FORMAT = "%Y%m%d %H:%M:%S" 70 | DATE_TIME_FORMAT_LONG = "%Y-%m-%d %H:%M:%S" 71 | DATE_TIME_FORMAT_LONG_MILLISECS = "%Y-%m-%d %H:%M:%S.%f" 72 | 73 | GENERIC_TICKS_NONE = '' 74 | GENERIC_TICKS_RTVOLUME = "233" 75 | 76 | SNAPSHOT_NONE = False 77 | SNAPSHOT_TRUE = True 78 | 79 | ORDER_TYPE_MARKET = "MKT" 80 | ORDER_TYPE_LIMIT = "LMT" 81 | ORDER_TYPE_STOP_LIMIT = "STP LMT" 82 | 83 | ORDER_ACTION_SELL = "SELL" 84 | ORDER_ACTION_BUY = "BUY" 85 | 86 | ERROR_CODE_MARKET_DATA_FARM_CONNECTED = 2104 87 | ERROR_CODE_HISTORICAL_DATA_FARM_CONNECTED = 2106 88 | ERROR_CODE_ORDER_CANCELED = 202 89 | 90 | -------------------------------------------------------------------------------- /src/ibHFT.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | import numpy as np 7 | from ib.opt import ibConnection, message 8 | from ib.opt import Connection 9 | import time 10 | from time import strftime 11 | from StockTradable import * 12 | from StockPosition import * 13 | from StockOrder import * 14 | 15 | 16 | class IbHFT: 17 | 18 | def __init__(self): 19 | self.is_use_gateway = True 20 | self.is_shutdown = False 21 | self.account_code = "" 22 | self.conn = None 23 | self.number_of_ticks = 0 24 | self.order_id = 0 25 | self.orders_pending = np.array([]) 26 | self.stock_ticks_dict = {} 27 | self.orders_filled = np.array([]) 28 | self.on_tick_func = None 29 | self.positions_dict = {} 30 | self.stock_codes = np.array([]) 31 | self.on_position_changed_func = None 32 | self.on_historical_data_func = None 33 | return 34 | 35 | ''' 36 | Callable methods from inherited parent. 37 | ''' 38 | def get_current_bid_price(self, stock_code): 39 | return self.stock_ticks_dict[stock_code].bid_price 40 | 41 | def get_current_ask_price(self, stock_code): 42 | return self.stock_ticks_dict[stock_code].ask_price 43 | 44 | def get_current_last_price(self, stock_code): 45 | return self.stock_ticks_dict[stock_code].last_price 46 | 47 | def place_limit_order(self, stock_code, is_buy, qty, price): 48 | this_order_id = self.get_order_id_and_increment() 49 | tradable = self.get_tradable(stock_code) 50 | stock_order = StockOrder(this_order_id, stock_code, is_buy, qty, price) 51 | self.place_order_and_add_to_list(this_order_id, tradable, stock_order) 52 | 53 | def place_order_and_add_to_list(self, this_order_id, tradable, stock_order): 54 | self.place_order(this_order_id, tradable, stock_order) 55 | self.add_pending_order(stock_order) 56 | 57 | def set_is_shutdown(self): 58 | self.is_shutdown = True 59 | 60 | def get_number_of_ticks(self): 61 | return self.number_of_ticks 62 | 63 | def update_order_with_price_and_type(self, order, new_price, stop_price): 64 | order.price = new_price 65 | order.stop_price = stop_price 66 | self.replace_order(order) 67 | 68 | def replace_order(self, order): 69 | tradable = self.get_tradable(order.stock_code) 70 | self.conn.placeOrder(order.order_id 71 | , tradable 72 | , order.get_stock_order()) 73 | 74 | def remove_all_pending_orders(self): 75 | for order in self.orders_pending: 76 | self.cancel_order(order.order_id) 77 | 78 | self.orders_pending = np.array([]) 79 | 80 | def remove_pending_order(self, order_id_to_delete): 81 | for i, order in enumerate(self.orders_pending): 82 | if order.order_id == order_id_to_delete: 83 | self.cancel_order(order.order_id) 84 | self.orders_pending = np.delete(self.orders_pending, i) 85 | return 86 | 87 | def add_pending_order(self, stock_order): 88 | self.orders_pending = np.hstack((self.orders_pending, stock_order)) 89 | 90 | def get_pending_orders(self): 91 | return self.orders_pending 92 | 93 | def get_order_id_and_increment(self): 94 | working_order_id = self.order_id 95 | self.order_id += 1 96 | return working_order_id 97 | 98 | def get_tradable(self, stock_code): 99 | return self.positions_dict[stock_code].tradable 100 | 101 | def get_number_of_pending_orders(self): 102 | return len(self.orders_pending) 103 | 104 | def get_number_of_filled_orders(self): 105 | return len(self.orders_filled) 106 | 107 | # def place_market_order(self, stock_code, is_buy, qty): 108 | # stock_contract = self.get_tradable(stock_code) 109 | # order = create_stock_order(qty, is_buy, True) 110 | # self.conn.placeOrder(self.order_id, stock_contract, order) 111 | 112 | def is_position_flat(self, stock_code): 113 | return self.get_position(stock_code) == 0 114 | 115 | def get_position(self, stock_code): 116 | return self.get_stock_position(stock_code).position 117 | 118 | def get_stock_position(self, stock_code): 119 | return self.positions_dict[stock_code] 120 | 121 | ''' 122 | Local methods 123 | ''' 124 | def logger(self, msg): 125 | 126 | if msg.typeName == DataType.MSG_TYPE_HISTORICAL_DATA: 127 | if self.on_historical_data_func is not None: 128 | self.on_historical_data_func(msg) 129 | return 130 | 131 | elif msg.typeName == DataType.MSG_TYPE_UPDATE_PORTFOLIO: 132 | self.process_portfolio_updates(msg) 133 | 134 | elif msg.typeName == DataType.MSG_TYPE_ACCOUNT_UPDATE: 135 | self.process_account_updates(msg) 136 | 137 | elif msg.typeName == DataType.MSG_TYPE_MANAGED_ACCOUNTS: 138 | self.set_account_code(msg.accountsList) 139 | 140 | elif msg.typeName == DataType.MSG_TYPE_NEXT_ORDER_ID: 141 | self.set_order_id(msg.orderId) 142 | 143 | elif msg.typeName == DataType.MSG_TYPE_ORDER_STATUS: 144 | self.update_order_status(msg) 145 | 146 | elif msg.typeName == DataType.MSG_TYPE_ERROR: 147 | self.process_error_message(msg) 148 | 149 | else: 150 | print "logger: " , msg 151 | 152 | def process_error_message(self, msg): 153 | 154 | if msg.errorCode == DataType.ERROR_CODE_MARKET_DATA_FARM_CONNECTED: 155 | print msg.errorMsg 156 | 157 | elif msg.errorCode == DataType.ERROR_CODE_HISTORICAL_DATA_FARM_CONNECTED: 158 | print msg.errorMsg 159 | 160 | elif msg.errorCode == DataType.ERROR_CODE_ORDER_CANCELED: 161 | print msg.errorMsg 162 | 163 | else: 164 | print "Unhandled errcode: ", msg 165 | 166 | def process_account_updates(self, msg): 167 | # Do nothing for now 168 | return 169 | 170 | def process_portfolio_updates(self, msg): 171 | try: 172 | for stock_code in self.positions_dict: 173 | if stock_code == msg.contract.m_symbol: 174 | self.positions_dict[stock_code].position = msg.position 175 | self.positions_dict[stock_code].market_value = msg.marketValue 176 | self.positions_dict[stock_code].average_price = msg.averageCost 177 | self.positions_dict[stock_code].realized_pnl = msg.realizedPNL 178 | self.positions_dict[stock_code].unrealized_pnl = msg.unrealizedPNL 179 | return 180 | 181 | except Exception, e: 182 | print "process_portfolio_updates err:", e 183 | 184 | def get_positions(self): 185 | return self.positions_dict 186 | 187 | def set_account_code(self, new_acct_code): 188 | self.account_code = new_acct_code 189 | 190 | def set_order_id(self, new_order_id): 191 | self.order_id = new_order_id 192 | 193 | def cancel_order(self, order_id): 194 | self.conn.cancelOrder(order_id) 195 | 196 | def place_order(self, this_order_id, tradable, stock_order): 197 | self.conn.placeOrder(this_order_id 198 | , tradable 199 | , stock_order.get_stock_order()) 200 | 201 | def update_order_status(self, msg): 202 | if msg.status == DataType.ORDER_STATUS_FILLED and msg.remaining == 0: 203 | self.set_pending_order_as_working(msg.orderId) 204 | 205 | if self.on_position_changed_func is not None: 206 | self.on_position_changed_func() 207 | 208 | def set_pending_order_as_working(self, this_order_id): 209 | for i, stock_order in enumerate(self.orders_pending): 210 | if stock_order.order_id == this_order_id: 211 | pos = stock_order.get_order_position() 212 | stock_code = stock_order.stock_code 213 | self.update_position(stock_code, pos) 214 | 215 | self.orders_filled = np.hstack((self.orders_filled, stock_order)) 216 | self.orders_pending = np.delete(self.orders_pending, i) 217 | return 218 | 219 | def update_position(self, stock_code, pos): 220 | self.positions_dict[stock_code].position += pos 221 | 222 | def tick_event(self, msg): 223 | self.number_of_ticks += 1 224 | stock_code = self.stock_codes[msg.tickerId] 225 | 226 | if msg.typeName == DataType.MSG_TYPE_TICK_STRING: 227 | return 228 | 229 | elif msg.typeName == DataType.MSG_TYPE_TICK_PRICE: 230 | 231 | if msg.field == DataType.FIELD_BID_PRICE: 232 | self.stock_ticks_dict[stock_code].bid_price = msg.price 233 | 234 | elif msg.field == DataType.FIELD_ASK_PRICE: 235 | self.stock_ticks_dict[stock_code].ask_price = msg.price 236 | 237 | elif msg.field == DataType.FIELD_LAST_PRICE: 238 | self.stock_ticks_dict[stock_code].last_price = msg.price 239 | 240 | elif msg.typeName == DataType.MSG_TYPE_STICK_SIZE: 241 | 242 | if msg.field == DataType.FIELD_BID_SIZE: 243 | self.stock_ticks_dict[stock_code].bid_volume = msg.size 244 | 245 | elif msg.field == DataType.FIELD_ASK_SIZE: 246 | self.stock_ticks_dict[stock_code].ask_volume = msg.size 247 | 248 | elif msg.field == DataType.FIELD_LAST_SIZE: 249 | self.stock_ticks_dict[stock_code].last_volume = msg.size 250 | 251 | elif msg.field == DataType.FIELD_VOLUME: 252 | self.stock_ticks_dict[stock_code].volume = msg.size 253 | 254 | else: 255 | print "Unhandle tick_event: ", msg 256 | 257 | if self.on_tick_func is not None: 258 | self.on_tick_func(self.stock_ticks_dict, stock_code, msg.field) 259 | 260 | def register_event_handlers(self, ibconn, logger_func=None): 261 | if logger_func is None: 262 | logger_func = self.logger 263 | 264 | ibconn.registerAll(logger_func) 265 | ibconn.unregister(logger_func 266 | , message.tickSize 267 | , message.tickPrice 268 | , message.tickString 269 | , message.tickGeneric 270 | , message.tickOptionComputation 271 | , message.updateAccountTime 272 | , message.accountDownloadEnd 273 | , message.commissionReport) 274 | 275 | ibconn.register(self.tick_event, message.tickPrice, message.tickSize) 276 | 277 | def request_streaming_data(self, ibconn): 278 | for i, stock_code in enumerate(self.stock_codes): 279 | ibconn.reqMktData(i 280 | , self.positions_dict[stock_code].tradable 281 | , DataType.GENERIC_TICKS_NONE 282 | , DataType.SNAPSHOT_NONE) 283 | time.sleep(1) 284 | 285 | def request_account_updates(self, ibconn): 286 | ibconn.reqAccountUpdates(True, self.account_code) 287 | time.sleep(1) 288 | 289 | def cancel_market_data_request(self, ibconn): 290 | for i in range(len(self.stock_codes)): 291 | ibconn.cancelMktData(i) 292 | time.sleep(1) 293 | 294 | def init_context(self, codes): 295 | self.stock_codes = codes 296 | self.stock_ticks_dict = dict([(stock_code, StockTick()) for stock_code in self.stock_codes]) 297 | self.positions_dict = dict([(stock_code, StockPosition(stock_code)) for stock_code in self.stock_codes]) 298 | 299 | def setup_connection(self): 300 | return Connection.create(port=4001, clientId=101) if self.is_use_gateway else ibConnection() 301 | 302 | @staticmethod 303 | def disconnect(ibconn): 304 | ibconn.disconnect() 305 | 306 | def set_connection_with_api_gateway(self, is_use_api_gateway): 307 | self.is_use_gateway = is_use_api_gateway 308 | 309 | def request_historical_data(self, ibconn, duration, interval): 310 | for i, stock_code in enumerate(self.positions_dict): 311 | ibconn.reqHistoricalData(i 312 | , self.positions_dict[stock_code].tradable 313 | , strftime(DataType.DATE_TIME_FORMAT) 314 | , duration 315 | , interval 316 | , DataType.WHAT_TO_SHOW_TRADES 317 | , DataType.RTH_ALL 318 | , DataType.DATEFORMAT_STRING) 319 | time.sleep(1) 320 | 321 | def start_historical_data_stream(self 322 | , stock_codes_arr 323 | , duration=DataType.DURATION_1_DAY 324 | , interval=DataType.BAR_SIZE_1_MIN 325 | , handler_func=None): 326 | self.init_context(stock_codes_arr) 327 | 328 | self.on_historical_data_func = handler_func 329 | 330 | self.conn = self.setup_connection() 331 | self.is_shutdown = False 332 | 333 | try: 334 | self.register_event_handlers(self.conn) 335 | self.conn.connect() 336 | self.request_historical_data(self.conn, duration, interval) 337 | 338 | while not self.is_shutdown: 339 | time.sleep(1) 340 | 341 | except Exception, e: 342 | print "Err: ", e 343 | print "Disconnecting..." 344 | self.conn.disconnect() 345 | time.sleep(1) 346 | print "Disconnected." 347 | 348 | def assign_functions(self, func_a, func_b): 349 | self.on_position_changed_func = func_a 350 | self.on_tick_func = func_b 351 | 352 | def start_data_stream(self 353 | , on_started_func 354 | , on_tick 355 | , stock_codes_to_stream 356 | , on_pos_changed=None): 357 | 358 | self.init_context(stock_codes_to_stream) 359 | self.assign_functions(on_pos_changed, on_tick) 360 | 361 | self.conn = self.setup_connection() 362 | 363 | try: 364 | self.register_event_handlers(self.conn) 365 | self.conn.connect() 366 | self.request_streaming_data(self.conn) 367 | self.request_account_updates(self.conn) 368 | on_started_func() 369 | 370 | except Exception, e: 371 | print "Err: ", e 372 | 373 | print "Cancelling...", 374 | 375 | self.cancel_market_data_request(self.conn) 376 | 377 | print "Disconnecting..." 378 | self.disconnect(self.conn) 379 | time.sleep(1) 380 | 381 | print "Disconnected." -------------------------------------------------------------------------------- /src/ibUtil.py: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Author: James Ma 3 | # Email stuff here: jamesmawm@gmail.com 4 | ####################################### 5 | 6 | from ib.ext.Contract import Contract 7 | from ib.ext.Order import Order 8 | import ibDataTypes as DataType 9 | 10 | 11 | def make_ib_contract(contract_tuple): 12 | contract = Contract() 13 | contract.m_symbol = contract_tuple[0] 14 | contract.m_secType = contract_tuple[1] 15 | contract.m_exchange = contract_tuple[2] 16 | contract.m_currency = contract_tuple[3] 17 | contract.m_expiry = contract_tuple[4] 18 | contract.m_strike = contract_tuple[5] 19 | contract.m_right = contract_tuple[6] 20 | return contract 21 | 22 | 23 | def create_stock_contract(stock): 24 | contract_tuple = (stock, 'STK', 'SMART', 'USD', '', 0.0, '') 25 | stock_contract = make_ib_contract(contract_tuple) 26 | return stock_contract 27 | 28 | 29 | def create_stock_order(order_id, quantity, is_buy, price=None, stop_price=None): 30 | order = Order() 31 | 32 | order.m_outsideRth = True 33 | order.m_orderId = order_id 34 | order.m_totalQuantity = quantity 35 | order.m_action = DataType.ORDER_ACTION_BUY if is_buy else DataType.ORDER_ACTION_SELL 36 | 37 | if price is None: 38 | order.m_orderType = DataType.ORDER_TYPE_MARKET 39 | else: 40 | order.m_lmtPrice = price 41 | order.m_orderType = DataType.ORDER_TYPE_LIMIT 42 | 43 | if stop_price is not None: 44 | order.m_auxPrice = stop_price 45 | order.m_orderType = DataType.ORDER_TYPE_STOP_LIMIT 46 | 47 | return order -------------------------------------------------------------------------------- /src/ticks 10 mins - Jun 25 2014.csv: -------------------------------------------------------------------------------- 1 | ,C,JPM,USB,WFC,BAC,MS 2 | 20140625 20:10:55,42.84,46.77,57.42,52.07,15.61,32.06 3 | 20140625 20:10:56,42.84,46.77,57.42,52.07,15.63,32.07 4 | 20140625 20:10:57,42.84,46.77,57.42,52.06,15.63,32.06 5 | 20140625 20:10:58,42.84,46.77,57.42,52.06,15.63,32.06 6 | 20140625 20:10:59,42.87,46.77,57.42,52.11,15.63,32.06 7 | 20140625 20:11:00,42.87,46.77,57.41,52.06,15.61,32.06 8 | 20140625 20:11:01,42.87,46.77,57.41,52.06,15.61,32.05 9 | 20140625 20:11:02,42.87,46.77,57.41,52.06,15.61,32.05 10 | 20140625 20:11:03,42.87,46.75,57.41,52.05,15.61,32.05 11 | 20140625 20:11:04,42.87,46.76,57.41,52.05,15.61,32.04 12 | 20140625 20:11:05,42.87,46.75,57.41,52.06,15.61,32.04 13 | 20140625 20:11:06,42.87,46.77,57.41,52.06,15.61,32.05 14 | 20140625 20:11:07,42.87,46.77,57.41,52.06,15.61,32.05 15 | 20140625 20:11:08,42.88,46.75,57.41,52.05,15.61,32.06 16 | 20140625 20:11:09,42.88,46.75,57.42,52.05,15.61,32.06 17 | 20140625 20:11:10,42.88,46.75,57.42,52.06,15.61,32.06 18 | 20140625 20:11:11,42.88,46.73,57.42,52.05,15.61,32.07 19 | 20140625 20:11:12,42.88,46.73,57.42,52.05,15.61,32.07 20 | 20140625 20:11:13,42.88,46.73,57.42,52.05,15.61,32.07 21 | 20140625 20:11:14,42.88,46.73,57.42,52.05,15.61,32.06 22 | 20140625 20:11:15,42.88,46.75,57.42,52.05,15.63,32.06 23 | 20140625 20:11:16,42.88,46.75,57.42,52.05,15.63,32.06 24 | 20140625 20:11:17,42.88,46.75,57.42,52.05,15.63,32.06 25 | 20140625 20:11:18,42.88,46.73,57.42,52.05,15.63,32.05 26 | 20140625 20:11:19,42.88,46.74,57.41,52.05,15.64,32.05 27 | 20140625 20:11:20,42.88,46.74,57.41,52.05,15.64,32.06 28 | 20140625 20:11:21,42.88,46.75,57.41,52.05,15.64,32.06 29 | 20140625 20:11:22,42.88,46.74,57.39,52.05,15.64,32.06 30 | 20140625 20:11:23,42.88,46.74,57.39,52.0,15.63,32.06 31 | 20140625 20:11:24,42.88,46.75,57.39,52.0,15.63,32.06 32 | 20140625 20:11:25,42.88,46.73,57.39,52.0,15.63,32.06 33 | 20140625 20:11:26,42.88,46.74,57.39,52.0,15.63,32.06 34 | 20140625 20:11:27,42.88,46.73,57.39,52.05,15.63,32.06 35 | 20140625 20:11:28,42.92,46.74,57.38,52.05,15.63,32.06 36 | 20140625 20:11:29,42.92,46.74,57.38,52.05,15.63,32.06 37 | 20140625 20:11:30,42.92,46.74,57.38,52.05,15.63,32.06 38 | 20140625 20:11:31,42.92,46.72,57.39,52.05,15.63,32.05 39 | 20140625 20:11:32,42.92,46.72,57.39,52.04,15.64,32.05 40 | 20140625 20:11:33,42.92,46.75,57.39,52.04,15.64,32.05 41 | 20140625 20:11:34,42.92,46.75,57.39,52.04,15.64,32.05 42 | 20140625 20:11:35,42.92,46.75,57.4,52.08,15.64,32.05 43 | 20140625 20:11:36,42.92,46.73,57.39,52.08,15.64,32.06 44 | 20140625 20:11:37,42.92,46.72,57.39,52.09,15.65,32.07 45 | 20140625 20:11:38,42.92,46.73,57.4,52.09,15.65,32.07 46 | 20140625 20:11:39,42.92,46.73,57.4,52.09,15.65,32.07 47 | 20140625 20:11:40,42.92,46.73,57.4,52.09,15.65,32.07 48 | 20140625 20:11:41,42.92,46.73,57.39,52.1,15.63,32.05 49 | 20140625 20:11:42,42.92,46.72,57.39,52.1,15.63,32.05 50 | 20140625 20:11:43,42.92,46.73,57.39,52.11,15.63,32.05 51 | 20140625 20:11:44,42.92,46.73,57.39,52.11,15.63,32.05 52 | 20140625 20:11:45,42.92,46.72,57.4,52.11,15.65,32.06 53 | 20140625 20:11:46,42.92,46.72,57.4,52.11,15.65,32.06 54 | 20140625 20:11:47,42.9,46.73,57.39,52.12,15.63,32.06 55 | 20140625 20:11:48,42.9,46.72,57.39,52.11,15.63,32.06 56 | 20140625 20:11:49,42.9,46.72,57.39,52.11,15.63,32.06 57 | 20140625 20:11:50,42.9,46.72,57.39,52.11,15.64,32.05 58 | 20140625 20:11:51,42.9,46.72,57.39,52.11,15.64,32.05 59 | 20140625 20:11:52,42.9,46.72,57.4,52.11,15.64,32.05 60 | 20140625 20:11:53,42.9,46.73,57.39,52.11,15.64,32.05 61 | 20140625 20:11:54,42.9,46.72,57.37,52.11,15.63,32.05 62 | 20140625 20:11:55,42.9,46.72,57.37,52.11,15.63,32.05 63 | 20140625 20:11:56,42.86,46.73,57.38,52.12,15.63,32.05 64 | 20140625 20:11:57,42.86,46.72,57.38,52.12,15.63,32.05 65 | 20140625 20:11:58,42.86,46.72,57.38,52.12,15.63,32.05 66 | 20140625 20:11:59,42.86,46.73,57.4,52.12,15.63,32.06 67 | 20140625 20:12:00,42.86,46.73,57.4,52.12,15.63,32.06 68 | 20140625 20:12:01,42.86,46.73,57.39,52.11,15.63,32.06 69 | 20140625 20:12:02,42.86,46.73,57.39,52.11,15.63,32.06 70 | 20140625 20:12:03,42.86,46.72,57.37,52.14,15.63,32.06 71 | 20140625 20:12:04,42.86,46.72,57.37,52.14,15.63,32.06 72 | 20140625 20:12:05,42.86,46.75,57.37,52.09,15.63,32.06 73 | 20140625 20:12:06,42.86,46.73,57.37,52.1,15.63,32.04 74 | 20140625 20:12:07,42.86,46.73,57.37,52.1,15.63,32.06 75 | 20140625 20:12:08,42.86,46.73,57.37,52.1,15.63,32.06 76 | 20140625 20:12:09,42.86,46.74,57.37,52.09,15.63,32.06 77 | 20140625 20:12:10,42.86,46.74,57.37,52.09,15.63,32.06 78 | 20140625 20:12:11,42.86,46.73,57.37,52.09,15.63,32.06 79 | 20140625 20:12:12,42.86,46.74,57.37,52.09,15.63,32.06 80 | 20140625 20:12:13,42.86,46.75,57.37,52.09,15.63,32.05 81 | 20140625 20:12:14,42.86,46.73,57.37,52.09,15.63,32.05 82 | 20140625 20:12:15,42.86,46.73,57.37,52.09,15.63,32.05 83 | 20140625 20:12:16,42.86,46.75,57.38,52.09,15.63,32.06 84 | 20140625 20:12:17,42.86,46.75,57.38,52.09,15.63,32.06 85 | 20140625 20:12:18,42.86,46.75,57.38,52.09,15.63,32.06 86 | 20140625 20:12:19,42.86,46.75,57.38,52.09,15.63,32.06 87 | 20140625 20:12:20,42.86,46.75,57.37,52.08,15.64,32.05 88 | 20140625 20:12:21,42.86,46.75,57.38,52.08,15.64,32.04 89 | 20140625 20:12:22,42.86,46.75,57.38,52.07,15.61,32.04 90 | 20140625 20:12:23,42.86,46.75,57.38,52.08,15.61,32.04 91 | 20140625 20:12:24,42.86,46.75,57.38,52.08,15.61,32.04 92 | 20140625 20:12:25,42.87,46.75,57.37,52.07,15.61,32.05 93 | 20140625 20:12:26,42.87,46.75,57.37,52.09,15.61,32.05 94 | 20140625 20:12:27,42.87,46.75,57.37,52.08,15.61,32.05 95 | 20140625 20:12:28,42.87,46.75,57.37,52.08,15.61,32.05 96 | 20140625 20:12:29,42.89,46.75,57.38,52.07,15.61,32.05 97 | 20140625 20:12:30,42.89,46.75,57.38,52.08,15.61,32.05 98 | 20140625 20:12:31,42.89,46.75,57.37,52.07,15.61,32.05 99 | 20140625 20:12:32,42.89,46.77,57.37,52.06,15.61,32.06 100 | 20140625 20:12:33,42.89,46.77,57.37,52.06,15.61,32.06 101 | 20140625 20:12:34,42.89,46.75,57.37,52.06,15.61,32.05 102 | 20140625 20:12:35,42.89,46.75,57.37,52.07,15.61,32.05 103 | 20140625 20:12:36,42.89,46.75,57.37,52.07,15.61,32.05 104 | 20140625 20:12:37,42.89,46.75,57.38,52.08,15.6,32.05 105 | 20140625 20:12:38,42.89,46.75,57.38,52.06,15.6,32.05 106 | 20140625 20:12:39,42.89,46.74,57.41,52.06,15.6,32.05 107 | 20140625 20:12:40,42.89,46.75,57.41,52.06,15.6,32.06 108 | 20140625 20:12:41,42.89,46.75,57.42,52.06,15.6,32.06 109 | 20140625 20:12:42,42.89,46.75,57.41,52.06,15.6,32.05 110 | 20140625 20:12:43,42.89,46.75,57.41,52.07,15.6,32.05 111 | 20140625 20:12:44,42.89,46.75,57.41,52.07,15.6,32.05 112 | 20140625 20:12:45,42.89,46.73,57.41,52.03,15.6,32.05 113 | 20140625 20:12:46,42.89,46.77,57.41,52.03,15.6,32.05 114 | 20140625 20:12:47,42.89,46.75,57.41,52.03,15.59,32.05 115 | 20140625 20:12:48,42.89,46.75,57.41,52.08,15.59,32.07 116 | 20140625 20:12:49,42.89,46.77,57.41,52.08,15.59,32.07 117 | 20140625 20:12:50,42.89,46.77,57.41,52.09,15.59,32.07 118 | 20140625 20:12:51,42.89,46.77,57.41,52.1,15.59,32.09 119 | 20140625 20:12:52,42.89,46.77,57.41,52.1,15.59,32.09 120 | 20140625 20:12:53,42.89,46.77,57.41,52.07,15.58,32.07 121 | 20140625 20:12:54,42.89,46.77,57.41,52.08,15.58,32.07 122 | 20140625 20:12:55,42.89,46.77,57.41,52.08,15.58,32.07 123 | 20140625 20:12:56,43.01,46.75,57.41,52.08,15.58,32.07 124 | 20140625 20:12:57,43.01,46.76,57.41,52.08,15.58,32.07 125 | 20140625 20:12:58,43.01,46.76,57.41,52.08,15.58,32.07 126 | 20140625 20:12:59,43.01,46.75,57.41,52.07,15.59,32.11 127 | 20140625 20:13:00,43.01,46.75,57.41,52.07,15.59,32.11 128 | 20140625 20:13:01,43.01,46.75,57.41,52.08,15.59,32.09 129 | 20140625 20:13:02,43.01,46.75,57.41,52.08,15.59,32.09 130 | 20140625 20:13:03,43.01,46.76,57.41,52.08,15.57,32.09 131 | 20140625 20:13:04,43.01,46.77,57.41,52.08,15.57,32.09 132 | 20140625 20:13:05,43.01,46.76,57.41,52.08,15.57,32.07 133 | 20140625 20:13:06,43.01,46.76,57.41,52.07,15.57,32.07 134 | 20140625 20:13:07,43.01,46.75,57.42,52.07,15.57,32.09 135 | 20140625 20:13:08,43.02,46.75,57.41,52.07,15.57,32.09 136 | 20140625 20:13:09,43.02,46.75,57.42,52.09,15.59,32.09 137 | 20140625 20:13:10,43.02,46.75,57.43,52.07,15.59,32.09 138 | 20140625 20:13:11,43.02,46.75,57.43,52.07,15.59,32.09 139 | 20140625 20:13:12,43.04,46.75,57.41,52.06,15.59,32.07 140 | 20140625 20:13:13,43.04,46.75,57.41,52.07,15.57,32.07 141 | 20140625 20:13:14,43.04,46.75,57.41,52.06,15.57,32.09 142 | 20140625 20:13:15,43.04,46.75,57.43,52.07,15.57,32.09 143 | 20140625 20:13:16,43.04,46.75,57.43,52.07,15.57,32.09 144 | 20140625 20:13:17,43.06,46.75,57.43,52.07,15.57,32.11 145 | 20140625 20:13:18,43.06,46.75,57.43,52.07,15.57,32.11 146 | 20140625 20:13:19,43.06,46.75,57.43,52.07,15.56,32.09 147 | 20140625 20:13:20,43.06,46.75,57.43,52.07,15.56,32.09 148 | 20140625 20:13:21,43.06,46.75,57.43,52.07,15.56,32.09 149 | 20140625 20:13:22,43.06,46.75,57.43,52.04,15.56,32.09 150 | 20140625 20:13:23,43.02,46.75,57.43,52.04,15.56,32.09 151 | 20140625 20:13:24,43.02,46.75,57.43,52.04,15.56,32.09 152 | 20140625 20:13:25,43.02,46.75,57.43,52.04,15.56,32.09 153 | 20140625 20:13:26,43.02,46.77,57.43,52.04,15.56,32.09 154 | 20140625 20:13:27,43.02,46.77,57.43,52.04,15.56,32.09 155 | 20140625 20:13:28,43.02,46.77,57.43,52.04,15.56,32.09 156 | 20140625 20:13:29,43.02,46.77,57.43,52.04,15.56,32.09 157 | 20140625 20:13:30,43.02,46.78,57.43,52.06,15.55,32.09 158 | 20140625 20:13:31,43.02,46.78,57.43,52.05,15.57,32.09 159 | 20140625 20:13:32,43.02,46.78,57.43,52.05,15.57,32.09 160 | 20140625 20:13:33,43.02,46.78,57.43,52.04,15.55,32.09 161 | 20140625 20:13:34,43.02,46.78,57.43,52.05,15.55,32.09 162 | 20140625 20:13:35,43.02,46.78,57.43,52.05,15.55,32.09 163 | 20140625 20:13:36,43.02,46.78,57.43,52.05,15.55,32.09 164 | 20140625 20:13:37,43.02,46.77,57.43,52.06,15.55,32.09 165 | 20140625 20:13:38,43.02,46.78,57.43,52.05,15.55,32.09 166 | 20140625 20:13:39,43.02,46.77,57.43,52.05,15.55,32.09 167 | 20140625 20:13:40,43.02,46.78,57.43,52.07,15.55,32.09 168 | 20140625 20:13:41,43.02,46.78,57.43,52.07,15.57,32.09 169 | 20140625 20:13:42,43.02,46.79,57.43,52.07,15.57,32.09 170 | 20140625 20:13:43,43.02,46.78,57.43,52.05,15.57,32.11 171 | 20140625 20:13:44,43.02,46.78,57.43,52.05,15.57,32.09 172 | 20140625 20:13:45,43.02,46.79,57.43,52.05,15.57,32.09 173 | 20140625 20:13:46,43.02,46.79,57.43,52.05,15.57,32.09 174 | 20140625 20:13:47,43.02,46.78,57.42,52.05,15.55,32.09 175 | 20140625 20:13:48,43.02,46.78,57.42,52.05,15.55,32.08 176 | 20140625 20:13:49,43.02,46.8,57.42,52.09,15.55,32.08 177 | 20140625 20:13:50,43.02,46.79,57.42,52.07,15.55,32.08 178 | 20140625 20:13:51,43.02,46.79,57.42,52.07,15.56,32.09 179 | 20140625 20:13:52,43.02,46.79,57.42,52.07,15.56,32.09 180 | 20140625 20:13:53,43.09,46.8,57.42,52.06,15.56,32.09 181 | 20140625 20:13:54,43.09,46.8,57.42,52.05,15.56,32.09 182 | 20140625 20:13:55,43.09,46.8,57.42,52.05,15.56,32.09 183 | 20140625 20:13:56,43.11,46.8,57.42,52.06,15.56,32.09 184 | 20140625 20:13:57,43.11,46.8,57.41,52.05,15.55,32.07 185 | 20140625 20:13:58,43.11,46.81,57.41,52.05,15.55,32.07 186 | 20140625 20:13:59,43.11,46.8,57.41,52.05,15.55,32.07 187 | 20140625 20:14:00,43.11,46.8,57.41,52.05,15.55,32.07 188 | 20140625 20:14:01,43.11,46.8,57.41,52.05,15.55,32.09 189 | 20140625 20:14:02,43.11,46.8,57.41,52.05,15.55,32.09 190 | 20140625 20:14:03,43.11,46.82,57.41,52.06,15.56,32.09 191 | 20140625 20:14:04,43.11,46.8,57.41,52.06,15.56,32.09 192 | 20140625 20:14:05,43.11,46.8,57.41,52.06,15.56,32.09 193 | 20140625 20:14:06,43.11,46.8,57.41,52.06,15.56,32.09 194 | 20140625 20:14:07,43.11,46.8,57.41,52.06,15.56,32.09 195 | 20140625 20:14:08,43.11,46.8,57.41,52.06,15.56,32.09 196 | 20140625 20:14:09,43.11,46.81,57.41,52.05,15.55,32.09 197 | 20140625 20:14:10,43.11,46.81,57.41,52.05,15.55,32.09 198 | 20140625 20:14:11,43.11,46.81,57.42,52.02,15.57,32.09 199 | 20140625 20:14:12,43.11,46.81,57.41,52.02,15.57,32.09 200 | 20140625 20:14:13,43.11,46.8,57.41,52.03,15.56,32.11 201 | 20140625 20:14:14,43.11,46.81,57.41,52.03,15.56,32.11 202 | 20140625 20:14:15,43.11,46.8,57.42,52.04,15.56,32.11 203 | 20140625 20:14:16,43.11,46.8,57.42,52.05,15.56,32.11 204 | 20140625 20:14:17,43.11,46.79,57.42,52.05,15.56,32.09 205 | 20140625 20:14:18,43.11,46.81,57.41,52.05,15.56,32.09 206 | 20140625 20:14:19,43.11,46.8,57.41,52.04,15.56,32.11 207 | 20140625 20:14:20,43.11,46.81,57.41,52.04,15.56,32.11 208 | 20140625 20:14:21,43.11,46.81,57.41,52.05,15.56,32.11 209 | 20140625 20:14:22,43.09,46.81,57.41,52.05,15.56,32.09 210 | 20140625 20:14:23,43.09,46.81,57.41,52.05,15.55,32.09 211 | 20140625 20:14:24,43.09,46.81,57.41,52.05,15.55,32.09 212 | 20140625 20:14:25,43.09,46.8,57.4,52.04,15.55,32.09 213 | 20140625 20:14:26,43.09,46.8,57.4,52.04,15.55,32.09 214 | 20140625 20:14:27,43.09,46.8,57.41,52.04,15.55,32.09 215 | 20140625 20:14:28,43.09,46.81,57.41,52.04,15.55,32.07 216 | 20140625 20:14:29,43.09,46.8,57.41,52.06,15.55,32.07 217 | 20140625 20:14:30,43.09,46.8,57.41,52.06,15.55,32.07 218 | 20140625 20:14:31,43.09,46.8,57.41,52.06,15.55,32.07 219 | 20140625 20:14:32,43.09,46.8,57.41,52.05,15.55,32.07 220 | 20140625 20:14:33,43.09,46.8,57.41,52.05,15.55,32.07 221 | 20140625 20:14:34,43.09,46.79,57.41,52.06,15.55,32.07 222 | 20140625 20:14:35,43.09,46.79,57.41,52.06,15.55,32.07 223 | 20140625 20:14:36,43.09,46.79,57.41,52.06,15.55,32.07 224 | 20140625 20:14:37,43.09,46.79,57.41,52.06,15.55,32.07 225 | 20140625 20:14:38,43.09,46.79,57.41,52.06,15.55,32.07 226 | 20140625 20:14:39,43.09,46.8,57.41,52.06,15.54,32.09 227 | 20140625 20:14:40,43.09,46.8,57.41,52.05,15.55,32.09 228 | 20140625 20:14:41,43.11,46.8,57.41,52.05,15.55,32.09 229 | 20140625 20:14:42,43.11,46.8,57.4,52.09,15.55,32.09 230 | 20140625 20:14:43,43.11,46.8,57.4,52.09,15.54,32.09 231 | 20140625 20:14:44,43.11,46.8,57.4,52.09,15.54,32.09 232 | 20140625 20:14:45,43.11,46.8,57.4,52.07,15.54,32.09 233 | 20140625 20:14:46,43.11,46.78,57.4,52.07,15.54,32.09 234 | 20140625 20:14:47,43.11,46.79,57.4,52.07,15.54,32.09 235 | 20140625 20:14:48,43.11,46.79,57.4,52.07,15.54,32.09 236 | 20140625 20:14:49,43.11,46.79,57.4,52.07,15.54,32.09 237 | 20140625 20:14:50,43.11,46.79,57.4,52.07,15.54,32.09 238 | 20140625 20:14:51,43.09,46.8,57.41,52.06,15.54,32.09 239 | 20140625 20:14:52,43.09,46.79,57.42,52.06,15.54,32.09 240 | 20140625 20:14:53,43.09,46.8,57.42,52.06,15.54,32.1 241 | 20140625 20:14:54,43.09,46.8,57.42,52.07,15.54,32.1 242 | 20140625 20:14:55,43.09,46.8,57.42,52.07,15.54,32.1 243 | 20140625 20:14:56,43.09,46.8,57.42,52.07,15.54,32.09 244 | 20140625 20:14:57,43.09,46.8,57.42,52.07,15.54,32.09 245 | 20140625 20:14:58,43.09,46.8,57.41,52.08,15.54,32.11 246 | 20140625 20:14:59,43.09,46.79,57.41,52.08,15.54,32.09 247 | 20140625 20:15:00,43.09,46.79,57.41,52.08,15.54,32.09 248 | 20140625 20:15:01,43.09,46.79,57.43,52.08,15.54,32.1 249 | 20140625 20:15:02,43.09,46.79,57.43,52.08,15.54,32.1 250 | 20140625 20:15:03,43.09,46.79,57.43,52.08,15.54,32.1 251 | 20140625 20:15:04,43.09,46.79,57.41,52.07,15.54,32.09 252 | 20140625 20:15:05,43.09,46.79,57.41,52.07,15.54,32.09 253 | 20140625 20:15:06,43.09,46.79,57.41,52.07,15.54,32.09 254 | 20140625 20:15:07,43.09,46.79,57.41,52.06,15.54,32.09 255 | 20140625 20:15:08,43.09,46.79,57.42,52.06,15.54,32.09 256 | 20140625 20:15:09,43.09,46.79,57.42,52.06,15.54,32.09 257 | 20140625 20:15:10,43.09,46.79,57.41,52.09,15.54,32.09 258 | 20140625 20:15:11,43.09,46.79,57.41,52.09,15.54,32.09 259 | 20140625 20:15:12,43.09,46.79,57.41,52.1,15.54,32.09 260 | 20140625 20:15:13,43.09,46.79,57.41,52.11,15.54,32.09 261 | 20140625 20:15:14,43.09,46.79,57.41,52.1,15.54,32.09 262 | 20140625 20:15:15,43.09,46.79,57.41,52.09,15.54,32.09 263 | 20140625 20:15:16,43.09,46.79,57.41,52.09,15.54,32.09 264 | 20140625 20:15:17,43.09,46.79,57.43,52.09,15.54,32.09 265 | 20140625 20:15:18,43.09,46.79,57.42,52.09,15.54,32.09 266 | 20140625 20:15:19,43.09,46.79,57.42,52.1,15.54,32.1 267 | 20140625 20:15:20,43.16,46.79,57.42,52.1,15.54,32.1 268 | 20140625 20:15:21,43.17,46.79,57.42,52.09,15.54,32.09 269 | 20140625 20:15:22,43.17,46.79,57.41,52.1,15.54,32.1 270 | 20140625 20:15:23,43.18,46.79,57.41,52.1,15.54,32.1 271 | 20140625 20:15:24,43.18,46.79,57.4,52.1,15.54,32.1 272 | 20140625 20:15:25,43.18,46.79,57.4,52.07,15.54,32.11 273 | 20140625 20:15:26,43.18,46.79,57.4,52.07,15.54,32.11 274 | 20140625 20:15:27,43.18,46.79,57.38,52.07,15.54,32.11 275 | 20140625 20:15:28,43.14,46.79,57.38,52.07,15.54,32.11 276 | 20140625 20:15:29,43.14,46.79,57.38,52.09,15.54,32.11 277 | 20140625 20:15:30,43.14,46.79,57.37,52.08,15.54,32.1 278 | 20140625 20:15:31,43.14,46.79,57.37,52.08,15.54,32.1 279 | 20140625 20:15:32,43.16,46.79,57.37,52.09,15.54,32.1 280 | 20140625 20:15:33,43.16,46.79,57.37,52.08,15.54,32.1 281 | 20140625 20:15:34,43.16,46.79,57.37,52.08,15.54,32.1 282 | 20140625 20:15:35,43.14,46.79,57.37,52.09,15.54,32.1 283 | 20140625 20:15:36,43.14,46.79,57.38,52.09,15.54,32.1 284 | 20140625 20:15:37,43.15,46.79,57.38,52.09,15.54,32.1 285 | 20140625 20:15:38,43.15,46.79,57.37,52.09,15.54,32.1 286 | 20140625 20:15:39,43.15,46.79,57.37,52.09,15.54,32.1 287 | 20140625 20:15:40,43.14,46.79,57.37,52.09,15.54,32.1 288 | 20140625 20:15:41,43.14,46.79,57.37,52.07,15.54,32.09 289 | 20140625 20:15:42,43.14,46.79,57.38,52.07,15.54,32.09 290 | 20140625 20:15:43,43.14,46.79,57.38,52.05,15.54,32.09 291 | 20140625 20:15:44,43.14,46.79,57.38,52.07,15.54,32.1 292 | 20140625 20:15:45,43.14,46.79,57.37,52.07,15.54,32.1 293 | 20140625 20:15:46,43.13,46.79,57.37,52.06,15.54,32.1 294 | 20140625 20:15:47,43.13,46.79,57.37,52.06,15.54,32.1 295 | 20140625 20:15:48,43.13,46.79,57.38,52.06,15.54,32.1 296 | 20140625 20:15:49,43.17,46.79,57.38,52.09,15.54,32.1 297 | 20140625 20:15:50,43.17,46.79,57.38,52.07,15.54,32.09 298 | 20140625 20:15:51,43.17,46.79,57.38,52.07,15.54,32.09 299 | 20140625 20:15:52,43.17,46.79,57.38,52.07,15.54,32.09 300 | 20140625 20:15:53,43.17,46.79,57.38,52.05,15.54,32.09 301 | 20140625 20:15:54,43.16,46.79,57.38,52.05,15.54,32.09 302 | 20140625 20:15:55,43.16,46.79,57.38,52.04,15.54,32.09 303 | 20140625 20:15:56,43.16,46.79,57.38,52.05,15.54,32.09 304 | 20140625 20:15:57,43.16,46.79,57.38,52.05,15.54,32.09 305 | 20140625 20:15:58,43.15,46.79,57.38,52.05,15.54,32.09 306 | 20140625 20:15:59,43.15,46.79,57.38,52.05,15.54,32.09 307 | 20140625 20:16:00,43.15,46.79,57.38,52.05,15.54,32.09 308 | 20140625 20:16:01,43.14,46.79,57.37,52.06,15.54,32.09 309 | 20140625 20:16:02,43.14,46.79,57.37,52.06,15.54,32.1 310 | 20140625 20:16:03,43.14,46.79,57.37,52.05,15.54,32.1 311 | 20140625 20:16:04,43.14,46.79,57.37,52.04,15.54,32.1 312 | 20140625 20:16:05,43.14,46.79,57.37,52.04,15.54,32.09 313 | 20140625 20:16:06,43.12,46.79,57.38,52.04,15.54,32.09 314 | 20140625 20:16:07,43.12,46.79,57.38,52.06,15.54,32.09 315 | 20140625 20:16:08,43.12,46.79,57.38,52.06,15.54,32.09 316 | 20140625 20:16:09,43.12,46.79,57.38,52.06,15.54,32.1 317 | 20140625 20:16:10,43.12,46.79,57.38,52.02,15.54,32.1 318 | 20140625 20:16:11,43.12,46.79,57.38,52.08,15.54,32.1 319 | 20140625 20:16:12,43.12,46.79,57.38,52.08,15.54,32.1 320 | 20140625 20:16:13,43.12,46.79,57.38,52.09,15.54,32.11 321 | 20140625 20:16:14,43.11,46.79,57.38,52.06,15.54,32.11 322 | 20140625 20:16:15,43.11,46.79,57.38,52.06,15.54,32.11 323 | 20140625 20:16:16,43.11,46.79,57.38,52.06,15.54,32.09 324 | 20140625 20:16:17,43.11,46.79,57.38,52.05,15.54,32.09 325 | 20140625 20:16:18,43.09,46.79,57.38,52.08,15.54,32.09 326 | 20140625 20:16:19,43.09,46.79,57.38,52.08,15.54,32.09 327 | 20140625 20:16:20,43.09,46.79,57.38,52.08,15.54,32.09 328 | 20140625 20:16:21,43.09,46.79,57.38,52.07,15.54,32.09 329 | 20140625 20:16:22,43.09,46.79,57.38,52.06,15.54,32.1 330 | 20140625 20:16:23,43.09,46.79,57.38,52.07,15.54,32.09 331 | 20140625 20:16:24,43.09,46.79,57.38,52.07,15.54,32.09 332 | 20140625 20:16:25,43.09,46.79,57.38,52.07,15.54,32.11 333 | 20140625 20:16:26,43.09,46.79,57.38,52.07,15.54,32.11 334 | 20140625 20:16:27,43.09,46.79,57.37,52.08,15.54,32.1 335 | 20140625 20:16:28,43.09,46.79,57.37,52.07,15.54,32.09 336 | 20140625 20:16:29,43.09,46.79,57.37,52.07,15.54,32.09 337 | 20140625 20:16:30,43.09,46.79,57.38,52.09,15.54,32.09 338 | 20140625 20:16:31,43.09,46.79,57.38,52.09,15.54,32.1 339 | 20140625 20:16:32,43.09,46.79,57.37,52.09,15.54,32.09 340 | 20140625 20:16:33,43.09,46.79,57.38,52.09,15.54,32.1 341 | 20140625 20:16:34,43.09,46.79,57.38,52.09,15.54,32.11 342 | 20140625 20:16:35,43.09,46.79,57.38,52.09,15.54,32.11 343 | 20140625 20:16:36,43.09,46.79,57.38,52.09,15.54,32.11 344 | 20140625 20:16:37,43.09,46.79,57.38,52.09,15.54,32.11 345 | 20140625 20:16:38,43.09,46.79,57.38,52.1,15.54,32.11 346 | 20140625 20:16:39,43.09,46.79,57.38,52.1,15.54,32.11 347 | 20140625 20:16:40,43.09,46.79,57.38,52.1,15.54,32.11 348 | 20140625 20:16:41,43.09,46.79,57.38,52.1,15.54,32.11 349 | 20140625 20:16:42,43.09,46.79,57.39,52.09,15.54,32.09 350 | 20140625 20:16:43,43.09,46.79,57.39,52.09,15.54,32.09 351 | 20140625 20:16:44,43.09,46.79,57.39,52.09,15.54,32.09 352 | 20140625 20:16:45,43.09,46.79,57.39,52.09,15.54,32.09 353 | 20140625 20:16:46,43.09,46.79,57.37,52.09,15.54,32.09 354 | 20140625 20:16:47,43.09,46.79,57.37,52.09,15.54,32.09 355 | 20140625 20:16:48,43.09,46.79,57.37,52.07,15.54,32.09 356 | 20140625 20:16:49,43.09,46.79,57.37,52.07,15.54,32.09 357 | 20140625 20:16:50,43.09,46.79,57.37,52.07,15.54,32.09 358 | 20140625 20:16:51,43.09,46.79,57.37,52.07,15.54,32.09 359 | 20140625 20:16:52,43.09,46.79,57.37,52.07,15.54,32.09 360 | 20140625 20:16:53,43.09,46.79,57.38,52.09,15.54,32.09 361 | 20140625 20:16:54,43.12,46.79,57.38,52.09,15.54,32.09 362 | 20140625 20:16:55,43.12,46.79,57.38,52.09,15.54,32.09 363 | 20140625 20:16:56,43.12,46.79,57.38,52.07,15.54,32.09 364 | 20140625 20:16:57,43.16,46.79,57.37,52.07,15.54,32.09 365 | 20140625 20:16:58,43.16,46.79,57.39,52.07,15.54,32.09 366 | 20140625 20:16:59,43.16,46.79,57.39,52.07,15.54,32.1 367 | 20140625 20:17:00,43.16,46.79,57.38,52.07,15.54,32.1 368 | 20140625 20:17:01,43.16,46.79,57.38,52.07,15.54,32.1 369 | 20140625 20:17:02,43.16,46.79,57.38,52.09,15.54,32.1 370 | 20140625 20:17:03,43.16,46.79,57.37,52.06,15.54,32.09 371 | 20140625 20:17:04,43.16,46.79,57.37,52.06,15.54,32.09 372 | 20140625 20:17:05,43.16,46.79,57.37,52.06,15.54,32.1 373 | 20140625 20:17:06,43.16,46.79,57.38,52.11,15.54,32.09 374 | 20140625 20:17:07,43.16,46.79,57.38,52.11,15.54,32.09 375 | 20140625 20:17:08,43.16,46.79,57.38,52.04,15.54,32.1 376 | 20140625 20:17:09,43.16,46.79,57.37,52.05,15.54,32.09 377 | 20140625 20:17:10,43.16,46.79,57.37,52.05,15.54,32.09 378 | 20140625 20:17:11,43.16,46.79,57.37,52.05,15.54,32.1 379 | 20140625 20:17:12,43.16,46.79,57.38,52.05,15.54,32.09 380 | 20140625 20:17:13,43.16,46.79,57.38,52.04,15.54,32.09 381 | 20140625 20:17:14,43.16,46.79,57.38,52.05,15.54,32.1 382 | 20140625 20:17:15,43.16,46.79,57.38,52.05,15.54,32.1 383 | 20140625 20:17:16,43.14,46.79,57.38,52.04,15.54,32.1 384 | 20140625 20:17:17,43.14,46.79,57.38,52.02,15.54,32.1 385 | 20140625 20:17:18,43.14,46.79,57.38,52.02,15.54,32.11 386 | 20140625 20:17:19,43.14,46.79,57.38,52.02,15.54,32.1 387 | 20140625 20:17:20,43.14,46.79,57.38,52.02,15.54,32.1 388 | 20140625 20:17:21,43.14,46.79,57.37,52.05,15.54,32.09 389 | 20140625 20:17:22,43.14,46.79,57.37,52.02,15.54,32.1 390 | 20140625 20:17:23,43.14,46.79,57.37,52.02,15.54,32.09 391 | 20140625 20:17:24,43.14,46.79,57.38,52.02,15.54,32.09 392 | 20140625 20:17:25,43.14,46.79,57.38,52.03,15.54,32.1 393 | 20140625 20:17:26,43.14,46.79,57.38,52.03,15.54,32.1 394 | 20140625 20:17:27,43.14,46.79,57.39,52.02,15.54,32.09 395 | 20140625 20:17:28,43.14,46.79,57.39,52.02,15.54,32.09 396 | 20140625 20:17:29,43.14,46.79,57.39,52.02,15.54,32.11 397 | 20140625 20:17:30,43.14,46.79,57.39,52.02,15.54,32.11 398 | 20140625 20:17:31,43.14,46.79,57.39,52.02,15.54,32.1 399 | 20140625 20:17:32,43.14,46.79,57.39,52.02,15.54,32.1 400 | 20140625 20:17:33,43.14,46.79,57.39,52.02,15.54,32.09 401 | 20140625 20:17:34,43.14,46.79,57.39,52.02,15.54,32.09 402 | 20140625 20:17:35,43.14,46.79,57.39,52.01,15.54,32.09 403 | 20140625 20:17:36,43.14,46.79,57.38,52.02,15.54,32.09 404 | 20140625 20:17:37,43.14,46.79,57.38,52.02,15.54,32.11 405 | 20140625 20:17:38,43.14,46.79,57.38,52.02,15.54,32.11 406 | 20140625 20:17:39,43.14,46.79,57.39,52.01,15.54,32.11 407 | 20140625 20:17:40,43.14,46.79,57.39,52.01,15.54,32.11 408 | 20140625 20:17:41,43.14,46.79,57.39,52.0,15.54,32.1 409 | 20140625 20:17:42,43.14,46.79,57.38,52.01,15.54,32.1 410 | 20140625 20:17:43,43.14,46.79,57.38,52.01,15.54,32.1 411 | 20140625 20:17:44,43.14,46.79,57.38,52.0,15.54,32.1 412 | 20140625 20:17:45,43.09,46.79,57.38,52.02,15.54,32.1 413 | 20140625 20:17:46,43.09,46.79,57.38,52.01,15.54,32.1 414 | 20140625 20:17:47,43.09,46.79,57.38,52.0,15.54,32.1 415 | 20140625 20:17:48,43.09,46.79,57.38,52.0,15.54,32.1 416 | 20140625 20:17:49,43.09,46.79,57.38,52.0,15.54,32.1 417 | 20140625 20:17:50,43.09,46.79,57.38,52.02,15.54,32.09 418 | 20140625 20:17:51,43.09,46.79,57.39,52.02,15.54,32.09 419 | 20140625 20:17:52,43.09,46.79,57.39,52.03,15.54,32.09 420 | 20140625 20:17:53,43.09,46.79,57.39,52.03,15.54,32.11 421 | 20140625 20:17:54,43.09,46.79,57.39,52.03,15.54,32.09 422 | 20140625 20:17:55,43.09,46.79,57.39,52.03,15.54,32.09 423 | 20140625 20:17:56,43.09,46.79,57.39,52.03,15.54,32.1 424 | 20140625 20:17:57,43.09,46.79,57.39,52.02,15.54,32.1 425 | 20140625 20:17:58,43.09,46.79,57.39,52.02,15.54,32.1 426 | 20140625 20:17:59,43.09,46.79,57.39,52.02,15.54,32.1 427 | 20140625 20:18:00,43.09,46.79,57.4,52.02,15.54,32.1 428 | 20140625 20:18:01,43.09,46.79,57.4,52.02,15.54,32.1 429 | 20140625 20:18:02,43.09,46.79,57.39,52.02,15.54,32.1 430 | 20140625 20:18:03,43.09,46.79,57.4,52.02,15.54,32.1 431 | 20140625 20:18:04,43.09,46.79,57.4,52.02,15.54,32.1 432 | 20140625 20:18:05,43.09,46.79,57.4,52.02,15.54,32.1 433 | 20140625 20:18:06,43.09,46.79,57.4,52.02,15.54,32.1 434 | 20140625 20:18:07,43.09,46.79,57.4,52.02,15.54,32.1 435 | 20140625 20:18:08,43.09,46.79,57.4,52.05,15.54,32.11 436 | 20140625 20:18:09,43.09,46.79,57.4,52.06,15.54,32.11 437 | 20140625 20:18:10,43.09,46.79,57.4,52.06,15.54,32.11 438 | 20140625 20:18:11,43.09,46.79,57.4,52.04,15.54,32.1 439 | 20140625 20:18:12,43.09,46.79,57.39,52.04,15.54,32.1 440 | 20140625 20:18:13,43.09,46.79,57.39,52.04,15.54,32.1 441 | 20140625 20:18:14,43.14,46.79,57.39,52.05,15.54,32.1 442 | 20140625 20:18:15,43.14,46.79,57.4,52.03,15.54,32.1 443 | 20140625 20:18:16,43.14,46.79,57.4,52.03,15.54,32.1 444 | 20140625 20:18:17,43.14,46.79,57.39,52.03,15.54,32.11 445 | 20140625 20:18:18,43.14,46.79,57.39,52.05,15.54,32.11 446 | 20140625 20:18:19,43.17,46.79,57.39,52.05,15.54,32.11 447 | 20140625 20:18:20,43.17,46.79,57.39,52.03,15.54,32.11 448 | 20140625 20:18:21,43.17,46.79,57.39,52.03,15.54,32.09 449 | 20140625 20:18:22,43.17,46.79,57.39,52.03,15.54,32.09 450 | 20140625 20:18:23,43.17,46.79,57.39,52.03,15.54,32.09 451 | 20140625 20:18:24,43.17,46.79,57.4,52.04,15.54,32.09 452 | 20140625 20:18:25,43.17,46.79,57.4,52.04,15.54,32.1 453 | 20140625 20:18:26,43.17,46.79,57.4,52.04,15.54,32.1 454 | 20140625 20:18:27,43.17,46.79,57.4,52.04,15.54,32.1 455 | 20140625 20:18:28,43.17,46.79,57.38,52.02,15.54,32.1 456 | 20140625 20:18:29,43.17,46.79,57.38,52.02,15.54,32.1 457 | 20140625 20:18:30,43.17,46.79,57.37,52.03,15.54,32.1 458 | 20140625 20:18:31,43.17,46.79,57.37,52.03,15.54,32.1 459 | 20140625 20:18:32,43.17,46.79,57.37,52.03,15.54,32.09 460 | 20140625 20:18:33,43.17,46.79,57.36,52.04,15.54,32.09 461 | 20140625 20:18:34,43.17,46.79,57.36,52.04,15.54,32.09 462 | 20140625 20:18:35,43.17,46.79,57.36,52.04,15.54,32.09 463 | 20140625 20:18:36,43.17,46.79,57.36,52.06,15.54,32.09 464 | 20140625 20:18:37,43.17,46.79,57.36,52.04,15.54,32.09 465 | 20140625 20:18:38,43.17,46.79,57.36,52.05,15.54,32.09 466 | 20140625 20:18:39,43.17,46.79,57.36,52.04,15.54,32.1 467 | 20140625 20:18:40,43.17,46.79,57.36,52.05,15.54,32.1 468 | 20140625 20:18:41,43.17,46.79,57.36,52.05,15.54,32.1 469 | 20140625 20:18:42,43.17,46.79,57.36,52.04,15.54,32.1 470 | 20140625 20:18:43,43.18,46.79,57.36,52.04,15.54,32.09 471 | 20140625 20:18:44,43.18,46.79,57.36,52.04,15.54,32.09 472 | 20140625 20:18:45,43.18,46.79,57.36,52.04,15.54,32.09 473 | 20140625 20:18:46,43.18,46.79,57.36,52.04,15.54,32.11 474 | 20140625 20:18:47,43.18,46.79,57.36,52.04,15.54,32.11 475 | 20140625 20:18:48,43.18,46.79,57.36,52.04,15.54,32.11 476 | 20140625 20:18:49,43.18,46.79,57.36,52.03,15.54,32.1 477 | 20140625 20:18:50,43.18,46.79,57.36,52.03,15.54,32.09 478 | 20140625 20:18:51,43.18,46.79,57.36,52.03,15.54,32.09 479 | 20140625 20:18:52,43.18,46.79,57.36,52.04,15.54,32.09 480 | 20140625 20:18:53,43.18,46.79,57.36,52.04,15.54,32.09 481 | 20140625 20:18:54,43.18,46.79,57.36,52.04,15.54,32.09 482 | 20140625 20:18:55,43.18,46.79,57.36,52.03,15.54,32.1 483 | 20140625 20:18:56,43.18,46.79,57.36,52.03,15.54,32.1 484 | 20140625 20:18:57,43.18,46.79,57.36,52.03,15.54,32.1 485 | 20140625 20:18:58,43.18,46.79,57.36,52.03,15.54,32.09 486 | 20140625 20:18:59,43.18,46.79,57.36,52.03,15.54,32.09 487 | 20140625 20:19:00,43.18,46.79,57.36,52.03,15.54,32.09 488 | 20140625 20:19:01,43.18,46.79,57.36,52.04,15.54,32.09 489 | 20140625 20:19:02,43.18,46.79,57.36,52.03,15.54,32.11 490 | 20140625 20:19:03,43.2,46.79,57.36,52.0,15.54,32.11 491 | 20140625 20:19:04,43.2,46.79,57.36,52.0,15.54,32.11 492 | 20140625 20:19:05,43.2,46.79,57.34,52.02,15.54,32.09 493 | 20140625 20:19:06,43.2,46.79,57.34,52.02,15.54,32.09 494 | 20140625 20:19:07,43.19,46.79,57.34,52.0,15.54,32.09 495 | 20140625 20:19:08,43.19,46.79,57.34,52.0,15.54,32.09 496 | 20140625 20:19:09,43.19,46.79,57.34,52.0,15.54,32.09 497 | 20140625 20:19:10,43.19,46.79,57.34,52.0,15.54,32.09 498 | 20140625 20:19:11,43.19,46.79,57.34,52.0,15.54,32.1 499 | 20140625 20:19:12,43.16,46.79,57.35,52.0,15.54,32.1 500 | 20140625 20:19:13,43.16,46.79,57.35,52.0,15.54,32.1 501 | 20140625 20:19:14,43.16,46.79,57.35,52.0,15.54,32.1 502 | 20140625 20:19:15,43.16,46.79,57.35,52.0,15.54,32.1 503 | 20140625 20:19:16,43.16,46.79,57.34,52.0,15.54,32.1 504 | 20140625 20:19:17,43.16,46.79,57.34,52.02,15.54,32.1 505 | 20140625 20:19:18,43.16,46.79,57.35,52.0,15.54,32.1 506 | 20140625 20:19:19,43.16,46.79,57.35,52.0,15.54,32.11 507 | 20140625 20:19:20,43.19,46.79,57.35,52.0,15.54,32.11 508 | 20140625 20:19:21,43.19,46.79,57.34,52.0,15.54,32.11 509 | 20140625 20:19:22,43.19,46.79,57.34,52.0,15.54,32.1 510 | 20140625 20:19:23,43.19,46.79,57.34,52.0,15.54,32.1 511 | 20140625 20:19:24,43.19,46.79,57.35,52.0,15.54,32.1 512 | 20140625 20:19:25,43.19,46.79,57.35,52.0,15.54,32.1 513 | 20140625 20:19:26,43.19,46.79,57.35,52.02,15.54,32.1 514 | 20140625 20:19:27,43.19,46.79,57.34,52.0,15.54,32.1 515 | 20140625 20:19:28,43.19,46.79,57.34,51.99,15.54,32.1 516 | 20140625 20:19:29,43.19,46.79,57.34,51.99,15.54,32.11 517 | 20140625 20:19:30,43.19,46.79,57.34,52.0,15.54,32.11 518 | 20140625 20:19:31,43.19,46.79,57.34,52.0,15.54,32.11 519 | 20140625 20:19:32,43.22,46.79,57.34,51.99,15.54,32.09 520 | 20140625 20:19:33,43.22,46.79,57.35,52.0,15.54,32.09 521 | 20140625 20:19:34,43.22,46.79,57.35,52.0,15.54,32.09 522 | 20140625 20:19:35,43.22,46.79,57.34,52.0,15.54,32.09 523 | 20140625 20:19:36,43.22,46.79,57.35,52.0,15.54,32.09 524 | 20140625 20:19:37,43.21,46.79,57.35,52.0,15.54,32.09 525 | 20140625 20:19:38,43.21,46.79,57.34,52.0,15.54,32.09 526 | 20140625 20:19:39,43.21,46.79,57.34,52.0,15.54,32.09 527 | 20140625 20:19:40,43.21,46.79,57.34,52.0,15.54,32.09 528 | 20140625 20:19:41,43.23,46.79,57.34,52.0,15.54,32.09 529 | 20140625 20:19:42,43.23,46.79,57.34,52.0,15.54,32.09 530 | 20140625 20:19:43,43.23,46.79,57.34,52.02,15.54,32.09 531 | 20140625 20:19:44,43.23,46.79,57.34,52.01,15.54,32.09 532 | 20140625 20:19:45,43.23,46.79,57.37,52.01,15.54,32.09 533 | 20140625 20:19:46,43.23,46.79,57.36,52.02,15.54,32.09 534 | 20140625 20:19:47,43.23,46.79,57.36,52.02,15.54,32.09 535 | 20140625 20:19:48,43.23,46.79,57.36,51.98,15.54,32.09 536 | 20140625 20:19:49,43.23,46.79,57.36,51.98,15.54,32.09 537 | 20140625 20:19:50,43.23,46.79,57.34,52.0,15.54,32.09 538 | 20140625 20:19:51,43.23,46.79,57.34,52.0,15.54,32.09 539 | 20140625 20:19:52,43.23,46.79,57.34,52.02,15.54,32.1 540 | 20140625 20:19:53,43.23,46.79,57.32,52.02,15.54,32.1 541 | 20140625 20:19:54,43.23,46.79,57.32,52.02,15.54,32.09 542 | 20140625 20:19:56,43.23,46.79,57.32,52.03,15.54,32.09 543 | 20140625 20:19:57,43.23,46.79,57.32,52.03,15.54,32.09 544 | 20140625 20:19:58,43.23,46.79,57.32,52.03,15.54,32.09 545 | 20140625 20:19:59,43.23,46.79,57.32,52.02,15.54,32.09 546 | 20140625 20:20:00,43.23,46.79,57.32,52.03,15.54,32.09 547 | 20140625 20:20:01,43.23,46.79,57.32,52.03,15.54,32.09 548 | 20140625 20:20:02,43.23,46.79,57.32,52.03,15.54,32.09 549 | 20140625 20:20:03,43.23,46.79,57.33,52.02,15.54,32.1 550 | 20140625 20:20:04,43.23,46.79,57.32,52.03,15.54,32.09 551 | 20140625 20:20:05,43.23,46.79,57.32,52.03,15.54,32.09 552 | 20140625 20:20:06,43.23,46.79,57.32,52.03,15.54,32.1 553 | 20140625 20:20:07,43.23,46.79,57.33,52.05,15.54,32.1 554 | 20140625 20:20:08,43.23,46.79,57.33,52.05,15.54,32.1 555 | 20140625 20:20:09,43.23,46.79,57.33,52.04,15.54,32.1 556 | 20140625 20:20:10,43.23,46.79,57.33,52.05,15.54,32.1 557 | 20140625 20:20:11,43.14,46.79,57.33,52.05,15.54,32.1 558 | 20140625 20:20:12,43.14,46.79,57.33,52.05,15.54,32.11 559 | 20140625 20:20:13,43.14,46.79,57.33,52.04,15.54,32.11 560 | 20140625 20:20:14,43.14,46.79,57.33,52.04,15.54,32.1 561 | 20140625 20:20:15,43.14,46.79,57.33,52.04,15.54,32.09 562 | 20140625 20:20:16,43.13,46.79,57.32,52.04,15.54,32.09 563 | 20140625 20:20:17,43.13,46.79,57.32,52.04,15.54,32.09 564 | 20140625 20:20:18,43.13,46.79,57.32,52.04,15.54,32.09 565 | 20140625 20:20:19,43.13,46.79,57.33,52.04,15.54,32.1 566 | 20140625 20:20:20,43.13,46.79,57.33,52.04,15.54,32.1 567 | 20140625 20:20:21,43.13,46.79,57.33,52.04,15.54,32.1 568 | 20140625 20:20:22,43.13,46.79,57.32,52.04,15.54,32.1 569 | 20140625 20:20:23,43.13,46.79,57.32,52.04,15.54,32.1 570 | 20140625 20:20:24,43.13,46.79,57.32,52.02,15.54,32.1 571 | 20140625 20:20:25,43.13,46.79,57.32,52.02,15.54,32.1 572 | 20140625 20:20:26,43.13,46.79,57.32,52.02,15.54,32.1 573 | 20140625 20:20:27,43.13,46.79,57.32,52.02,15.54,32.1 574 | 20140625 20:20:28,43.13,46.79,57.32,52.02,15.54,32.1 575 | 20140625 20:20:29,43.13,46.79,57.31,52.03,15.54,32.1 576 | 20140625 20:20:30,43.13,46.79,57.31,52.02,15.54,32.1 577 | 20140625 20:20:31,43.13,46.79,57.32,52.02,15.54,32.1 578 | 20140625 20:20:32,43.13,46.79,57.32,52.04,15.54,32.09 579 | 20140625 20:20:33,43.13,46.79,57.32,52.04,15.54,32.09 580 | 20140625 20:20:34,43.13,46.79,57.31,52.03,15.54,32.11 581 | 20140625 20:20:35,43.13,46.79,57.31,52.03,15.54,32.09 582 | 20140625 20:20:36,43.13,46.79,57.31,52.03,15.54,32.09 583 | 20140625 20:20:37,43.13,46.79,57.31,52.02,15.54,32.09 584 | 20140625 20:20:38,43.13,46.79,57.31,52.02,15.54,32.11 585 | 20140625 20:20:39,43.13,46.79,57.31,52.02,15.54,32.1 586 | 20140625 20:20:40,43.1,46.79,57.32,52.01,15.54,32.1 587 | 20140625 20:20:41,43.1,46.79,57.32,52.0,15.54,32.09 588 | 20140625 20:20:42,43.1,46.79,57.32,52.0,15.54,32.09 589 | 20140625 20:20:43,43.1,46.79,57.31,52.0,15.54,32.09 590 | 20140625 20:20:44,43.1,46.79,57.31,52.0,15.54,32.09 591 | 20140625 20:20:45,43.1,46.79,57.31,52.01,15.54,32.09 592 | 20140625 20:20:46,43.1,46.79,57.32,52.01,15.54,32.09 593 | 20140625 20:20:47,43.1,46.79,57.31,52.01,15.54,32.09 594 | 20140625 20:20:48,43.1,46.79,57.31,52.0,15.54,32.09 595 | 20140625 20:20:49,43.1,46.79,57.32,52.0,15.54,32.09 596 | 20140625 20:20:50,43.1,46.79,57.32,52.01,15.54,32.09 597 | 20140625 20:20:51,43.1,46.79,57.32,52.0,15.54,32.1 598 | 20140625 20:20:52,43.1,46.79,57.31,52.0,15.54,32.1 599 | 20140625 20:20:53,43.1,46.79,57.31,52.0,15.54,32.1 600 | 20140625 20:20:54,43.1,46.79,57.31,52.0,15.54,32.09 601 | 20140625 20:20:55,43.09,46.79,57.32,52.0,15.54,32.09 602 | --------------------------------------------------------------------------------