├── .gitignore ├── LICENSE ├── bin └── matcher ├── deps └── json2.js ├── index.js ├── lib ├── order.js └── order_book.js ├── package.json └── test ├── get_state.js ├── recover.js ├── recover ├── end.js ├── multi.json ├── none.json └── start.json ├── test.js ├── unit ├── add_cancel │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json ├── add_fill_cancel │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json ├── match_full │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json ├── match_over │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json ├── match_partial │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json ├── match_several │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json └── trade_with_self │ ├── journal.log │ ├── recv.json │ ├── recv_multi.json │ ├── send.json │ └── state.json └── update_precision.py /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | *.swp 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 Bitfloor, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /bin/matcher: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // 3rd party 4 | var nopt = require('nopt'); 5 | 6 | // common 7 | var config = require('bitfloor/config'); 8 | var logger = require('bitfloor/logger'); 9 | 10 | var Matcher = require('../index.js'); 11 | 12 | var known = { 13 | product: Number, 14 | }; 15 | 16 | var short = {}; 17 | 18 | // process options 19 | var options = nopt(known, short, process.argv, 2); 20 | 21 | var product_id = options.product; 22 | if (!product_id) { 23 | console.error('a product id must be specified with --product'); 24 | return process.exit(); 25 | } 26 | 27 | // the matcher config will tell us everything we need to know about 28 | // what host/port to listen on and send market data out on 29 | var matcher_config = config.get_matcher(product_id); 30 | if (!matcher_config) { 31 | console.error('no matcher config found for product id: ' + product_id); 32 | return process.exit(); 33 | } 34 | 35 | var matcher = new Matcher(product_id, matcher_config); 36 | matcher.start(function() { 37 | logger.trace('matcher running for product: ' + product_id); 38 | }); 39 | 40 | // vim: ft=javascript 41 | -------------------------------------------------------------------------------- /deps/json2.js: -------------------------------------------------------------------------------- 1 | /* 2 | http://www.JSON.org/json2.js 3 | 2011-02-23 4 | 5 | Public Domain. 6 | 7 | NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 8 | 9 | See http://www.JSON.org/js.html 10 | 11 | 12 | This code should be minified before deployment. 13 | See http://javascript.crockford.com/jsmin.html 14 | 15 | USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 16 | NOT CONTROL. 17 | 18 | 19 | This file creates a global JSON object containing two methods: stringify 20 | and parse. 21 | 22 | JSON.stringify(value, replacer, space) 23 | value any JavaScript value, usually an object or array. 24 | 25 | replacer an optional parameter that determines how object 26 | values are stringified for objects. It can be a 27 | function or an array of strings. 28 | 29 | space an optional parameter that specifies the indentation 30 | of nested structures. If it is omitted, the text will 31 | be packed without extra whitespace. If it is a number, 32 | it will specify the number of spaces to indent at each 33 | level. If it is a string (such as '\t' or ' '), 34 | it contains the characters used to indent at each level. 35 | 36 | This method produces a JSON text from a JavaScript value. 37 | 38 | When an object value is found, if the object contains a toJSON 39 | method, its toJSON method will be called and the result will be 40 | stringified. A toJSON method does not serialize: it returns the 41 | value represented by the name/value pair that should be serialized, 42 | or undefined if nothing should be serialized. The toJSON method 43 | will be passed the key associated with the value, and this will be 44 | bound to the value 45 | 46 | For example, this would serialize Dates as ISO strings. 47 | 48 | Date.prototype.toJSON = function (key) { 49 | function f(n) { 50 | // Format integers to have at least two digits. 51 | return n < 10 ? '0' + n : n; 52 | } 53 | 54 | return this.getUTCFullYear() + '-' + 55 | f(this.getUTCMonth() + 1) + '-' + 56 | f(this.getUTCDate()) + 'T' + 57 | f(this.getUTCHours()) + ':' + 58 | f(this.getUTCMinutes()) + ':' + 59 | f(this.getUTCSeconds()) + 'Z'; 60 | }; 61 | 62 | You can provide an optional replacer method. It will be passed the 63 | key and value of each member, with this bound to the containing 64 | object. The value that is returned from your method will be 65 | serialized. If your method returns undefined, then the member will 66 | be excluded from the serialization. 67 | 68 | If the replacer parameter is an array of strings, then it will be 69 | used to select the members to be serialized. It filters the results 70 | such that only members with keys listed in the replacer array are 71 | stringified. 72 | 73 | Values that do not have JSON representations, such as undefined or 74 | functions, will not be serialized. Such values in objects will be 75 | dropped; in arrays they will be replaced with null. You can use 76 | a replacer function to replace those with JSON values. 77 | JSON.stringify(undefined) returns undefined. 78 | 79 | The optional space parameter produces a stringification of the 80 | value that is filled with line breaks and indentation to make it 81 | easier to read. 82 | 83 | If the space parameter is a non-empty string, then that string will 84 | be used for indentation. If the space parameter is a number, then 85 | the indentation will be that many spaces. 86 | 87 | Example: 88 | 89 | text = JSON.stringify(['e', {pluribus: 'unum'}]); 90 | // text is '["e",{"pluribus":"unum"}]' 91 | 92 | 93 | text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); 94 | // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' 95 | 96 | text = JSON.stringify([new Date()], function (key, value) { 97 | return this[key] instanceof Date ? 98 | 'Date(' + this[key] + ')' : value; 99 | }); 100 | // text is '["Date(---current time---)"]' 101 | 102 | 103 | JSON.parse(text, reviver) 104 | This method parses a JSON text to produce an object or array. 105 | It can throw a SyntaxError exception. 106 | 107 | The optional reviver parameter is a function that can filter and 108 | transform the results. It receives each of the keys and values, 109 | and its return value is used instead of the original value. 110 | If it returns what it received, then the structure is not modified. 111 | If it returns undefined then the member is deleted. 112 | 113 | Example: 114 | 115 | // Parse the text. Values that look like ISO date strings will 116 | // be converted to Date objects. 117 | 118 | myData = JSON.parse(text, function (key, value) { 119 | var a; 120 | if (typeof value === 'string') { 121 | a = 122 | /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); 123 | if (a) { 124 | return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], 125 | +a[5], +a[6])); 126 | } 127 | } 128 | return value; 129 | }); 130 | 131 | myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { 132 | var d; 133 | if (typeof value === 'string' && 134 | value.slice(0, 5) === 'Date(' && 135 | value.slice(-1) === ')') { 136 | d = new Date(value.slice(5, -1)); 137 | if (d) { 138 | return d; 139 | } 140 | } 141 | return value; 142 | }); 143 | 144 | 145 | This is a reference implementation. You are free to copy, modify, or 146 | redistribute. 147 | */ 148 | 149 | /*jslint evil: true, strict: false, regexp: false */ 150 | 151 | /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, 152 | call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, 153 | getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, 154 | lastIndex, length, parse, prototype, push, replace, slice, stringify, 155 | test, toJSON, toString, valueOf 156 | */ 157 | 158 | 159 | // Create a JSON object only if one does not already exist. We create the 160 | // methods in a closure to avoid creating global variables. 161 | 162 | var JSON; 163 | if (!JSON) { 164 | JSON = {}; 165 | } 166 | 167 | (function () { 168 | "use strict"; 169 | 170 | function f(n) { 171 | // Format integers to have at least two digits. 172 | return n < 10 ? '0' + n : n; 173 | } 174 | 175 | if (typeof Date.prototype.toJSON !== 'function') { 176 | 177 | Date.prototype.toJSON = function (key) { 178 | 179 | return isFinite(this.valueOf()) ? 180 | this.getUTCFullYear() + '-' + 181 | f(this.getUTCMonth() + 1) + '-' + 182 | f(this.getUTCDate()) + 'T' + 183 | f(this.getUTCHours()) + ':' + 184 | f(this.getUTCMinutes()) + ':' + 185 | f(this.getUTCSeconds()) + 'Z' : null; 186 | }; 187 | 188 | String.prototype.toJSON = 189 | Number.prototype.toJSON = 190 | Boolean.prototype.toJSON = function (key) { 191 | return this.valueOf(); 192 | }; 193 | } 194 | 195 | var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 196 | escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 197 | gap, 198 | indent, 199 | meta = { // table of character substitutions 200 | '\b': '\\b', 201 | '\t': '\\t', 202 | '\n': '\\n', 203 | '\f': '\\f', 204 | '\r': '\\r', 205 | '"' : '\\"', 206 | '\\': '\\\\' 207 | }, 208 | rep; 209 | 210 | 211 | function quote(string) { 212 | 213 | // If the string contains no control characters, no quote characters, and no 214 | // backslash characters, then we can safely slap some quotes around it. 215 | // Otherwise we must also replace the offending characters with safe escape 216 | // sequences. 217 | 218 | escapable.lastIndex = 0; 219 | return escapable.test(string) ? '"' + string.replace(escapable, function (a) { 220 | var c = meta[a]; 221 | return typeof c === 'string' ? c : 222 | '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 223 | }) + '"' : '"' + string + '"'; 224 | } 225 | 226 | 227 | function str(key, holder) { 228 | 229 | // Produce a string from holder[key]. 230 | 231 | var i, // The loop counter. 232 | k, // The member key. 233 | v, // The member value. 234 | length, 235 | mind = gap, 236 | partial, 237 | value = holder[key]; 238 | 239 | // If the value has a toJSON method, call it to obtain a replacement value. 240 | 241 | if (value && typeof value === 'object' && 242 | typeof value.toJSON === 'function') { 243 | value = value.toJSON(key); 244 | } 245 | 246 | // If we were called with a replacer function, then call the replacer to 247 | // obtain a replacement value. 248 | 249 | if (typeof rep === 'function') { 250 | value = rep.call(holder, key, value); 251 | } 252 | 253 | // What happens next depends on the value's type. 254 | 255 | switch (typeof value) { 256 | case 'string': 257 | return quote(value); 258 | 259 | case 'number': 260 | 261 | // JSON numbers must be finite. Encode non-finite numbers as null. 262 | 263 | return isFinite(value) ? String(value) : 'null'; 264 | 265 | case 'boolean': 266 | case 'null': 267 | 268 | // If the value is a boolean or null, convert it to a string. Note: 269 | // typeof null does not produce 'null'. The case is included here in 270 | // the remote chance that this gets fixed someday. 271 | 272 | return String(value); 273 | 274 | // If the type is 'object', we might be dealing with an object or an array or 275 | // null. 276 | 277 | case 'object': 278 | 279 | // Due to a specification blunder in ECMAScript, typeof null is 'object', 280 | // so watch out for that case. 281 | 282 | if (!value) { 283 | return 'null'; 284 | } 285 | 286 | // Make an array to hold the partial results of stringifying this object value. 287 | 288 | gap += indent; 289 | partial = []; 290 | 291 | // Is the value an array? 292 | 293 | if (Object.prototype.toString.apply(value) === '[object Array]') { 294 | 295 | // The value is an array. Stringify every element. Use null as a placeholder 296 | // for non-JSON values. 297 | 298 | length = value.length; 299 | for (i = 0; i < length; i += 1) { 300 | partial[i] = str(i, value) || 'null'; 301 | } 302 | 303 | // Join all of the elements together, separated with commas, and wrap them in 304 | // brackets. 305 | 306 | v = partial.length === 0 ? '[]' : gap ? 307 | '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : 308 | '[' + partial.join(',') + ']'; 309 | gap = mind; 310 | return v; 311 | } 312 | 313 | // If the replacer is an array, use it to select the members to be stringified. 314 | 315 | if (rep && typeof rep === 'object') { 316 | length = rep.length; 317 | for (i = 0; i < length; i += 1) { 318 | if (typeof rep[i] === 'string') { 319 | k = rep[i]; 320 | v = str(k, value); 321 | if (v) { 322 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 323 | } 324 | } 325 | } 326 | } else { 327 | 328 | // Otherwise, iterate through all of the keys in the object. 329 | 330 | for (k in value) { 331 | if (Object.prototype.hasOwnProperty.call(value, k)) { 332 | v = str(k, value); 333 | if (v) { 334 | partial.push(quote(k) + (gap ? ': ' : ':') + v); 335 | } 336 | } 337 | } 338 | } 339 | 340 | // Join all of the member texts together, separated with commas, 341 | // and wrap them in braces. 342 | 343 | v = partial.length === 0 ? '{}' : gap ? 344 | '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : 345 | '{' + partial.join(',') + '}'; 346 | gap = mind; 347 | return v; 348 | } 349 | } 350 | 351 | // If the JSON object does not yet have a stringify method, give it one. 352 | 353 | if (typeof JSON.stringify !== 'function') { 354 | JSON.stringify = function (value, replacer, space) { 355 | 356 | // The stringify method takes a value and an optional replacer, and an optional 357 | // space parameter, and returns a JSON text. The replacer can be a function 358 | // that can replace values, or an array of strings that will select the keys. 359 | // A default replacer method can be provided. Use of the space parameter can 360 | // produce text that is more easily readable. 361 | 362 | var i; 363 | gap = ''; 364 | indent = ''; 365 | 366 | // If the space parameter is a number, make an indent string containing that 367 | // many spaces. 368 | 369 | if (typeof space === 'number') { 370 | for (i = 0; i < space; i += 1) { 371 | indent += ' '; 372 | } 373 | 374 | // If the space parameter is a string, it will be used as the indent string. 375 | 376 | } else if (typeof space === 'string') { 377 | indent = space; 378 | } 379 | 380 | // If there is a replacer, it must be a function or an array. 381 | // Otherwise, throw an error. 382 | 383 | rep = replacer; 384 | if (replacer && typeof replacer !== 'function' && 385 | (typeof replacer !== 'object' || 386 | typeof replacer.length !== 'number')) { 387 | throw new Error('JSON.stringify'); 388 | } 389 | 390 | // Make a fake root object containing our value under the key of ''. 391 | // Return the result of stringifying the value. 392 | 393 | return str('', {'': value}); 394 | }; 395 | } 396 | 397 | 398 | // If the JSON object does not yet have a parse method, give it one. 399 | 400 | if (typeof JSON.parse !== 'function') { 401 | JSON.parse = function (text, reviver) { 402 | 403 | // The parse method takes a text and an optional reviver function, and returns 404 | // a JavaScript value if the text is a valid JSON text. 405 | 406 | var j; 407 | 408 | function walk(holder, key) { 409 | 410 | // The walk method is used to recursively walk the resulting structure so 411 | // that modifications can be made. 412 | 413 | var k, v, value = holder[key]; 414 | if (value && typeof value === 'object') { 415 | for (k in value) { 416 | if (Object.prototype.hasOwnProperty.call(value, k)) { 417 | v = walk(value, k); 418 | if (v !== undefined) { 419 | value[k] = v; 420 | } else { 421 | delete value[k]; 422 | } 423 | } 424 | } 425 | } 426 | return reviver.call(holder, key, value); 427 | } 428 | 429 | 430 | // Parsing happens in four stages. In the first stage, we replace certain 431 | // Unicode characters with escape sequences. JavaScript handles many characters 432 | // incorrectly, either silently deleting them, or treating them as line endings. 433 | 434 | text = String(text); 435 | cx.lastIndex = 0; 436 | if (cx.test(text)) { 437 | text = text.replace(cx, function (a) { 438 | return '\\u' + 439 | ('0000' + a.charCodeAt(0).toString(16)).slice(-4); 440 | }); 441 | } 442 | 443 | // In the second stage, we run the text against regular expressions that look 444 | // for non-JSON patterns. We are especially concerned with '()' and 'new' 445 | // because they can cause invocation, and '=' because it can cause mutation. 446 | // But just to be safe, we want to reject all unexpected forms. 447 | 448 | // We split the second stage into 4 regexp operations in order to work around 449 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 450 | // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we 451 | // replace all simple value tokens with ']' characters. Third, we delete all 452 | // open brackets that follow a colon or comma or that begin the text. Finally, 453 | // we look to see that the remaining characters are only whitespace or ']' or 454 | // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 455 | 456 | if (/^[\],:{}\s]*$/ 457 | .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') 458 | .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') 459 | .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { 460 | 461 | // In the third stage we use the eval function to compile the text into a 462 | // JavaScript structure. The '{' operator is subject to a syntactic ambiguity 463 | // in JavaScript: it can begin a block or an object literal. We wrap the text 464 | // in parens to eliminate the ambiguity. 465 | 466 | j = eval('(' + text + ')'); 467 | 468 | // In the optional fourth stage, we recursively walk the new structure, passing 469 | // each name/value pair to a reviver function for possible transformation. 470 | 471 | return typeof reviver === 'function' ? 472 | walk({'': j}, '') : j; 473 | } 474 | 475 | // If the text is not JSON parseable, then a SyntaxError is thrown. 476 | 477 | throw new SyntaxError('JSON.parse'); 478 | }; 479 | } 480 | 481 | module.exports = JSON; 482 | }()); 483 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | // builtin 3 | var net = require('net'); 4 | var dgram = require('dgram'); 5 | var events = require('events'); 6 | var fs = require('fs'); 7 | var util = require('util'); 8 | var path = require('path'); 9 | 10 | // 3rd party 11 | var uuid = require('node-uuid'); 12 | var carrier = require('carrier'); 13 | var Chain = require('chain'); 14 | 15 | // common 16 | var Messenger = require('bitfloor/messenger'); 17 | var Journal = require('bitfloor/journal'); 18 | var logger = require('bitfloor/logger'); 19 | var config = require('bitfloor/config'); 20 | var time = require('bitfloor/time'); 21 | var env = config.env; 22 | 23 | // local 24 | var OrderBook = require('./lib/order_book').OrderBook; 25 | var Order = require('./lib/order'); 26 | 27 | var matcher_state_dir = config.env.statedir; 28 | var matcher_state_prefix = matcher_state_dir + '/matcher_state'; 29 | 30 | // write the state every N milliseconds 31 | var write_state_period = 1000*60*15; 32 | 33 | // interval to rotate the incoming journal 34 | // rotate every 12 hours 35 | var journal_rotate_interval = 1000 * 60 * 60 * 12; 36 | 37 | function Matcher(product_id, config) { 38 | this.server; 39 | this.config = config; 40 | this.product_id = product_id; 41 | 42 | // clear matcher state 43 | this.reset(); 44 | } 45 | 46 | // matcher emits the 'process' event for testing 47 | // see req_processor.js in lib 48 | Matcher.prototype = new events.EventEmitter(); 49 | 50 | // recovers matcher state 51 | // calls cb() on finish, exits the process if error 52 | Matcher.prototype.recover = function(send_feed_msg, register_event_handlers, cb) { 53 | logger.info('state recovery started'); 54 | 55 | var self = this; 56 | 57 | Chain.exec( 58 | function() { 59 | // get all files in the matcher's state directory 60 | fs.readdir(matcher_state_dir, Chain.next()) 61 | }, 62 | function(err, files) { 63 | if (err) { 64 | logger.panic('could not read matcher state dir', err); 65 | process.exit(1); 66 | } 67 | 68 | var state_files = []; 69 | 70 | // get the files that match the prefix 71 | files.forEach(function(file) { 72 | var re = new RegExp('matcher_state\\.' + self.product_id + '\\.(\\d+)\\.json'); 73 | var match = file.match(re); 74 | if (match) { 75 | var num = match[1] - 0; 76 | state_files.push({file: file, num: num}); 77 | } 78 | }); 79 | 80 | // no state file to load, fresh start 81 | if (!state_files.length) { 82 | logger.info('No state files found! Either this is the first time starting the matcher for this product or there is a serious error'); 83 | 84 | // always register the event handlers before calling callback 85 | register_event_handlers(); 86 | 87 | if (cb) 88 | cb(); 89 | return; 90 | } 91 | 92 | // get the one with the latest state_num 93 | state_files.sort(function(a, b) { return b.num - a.num; }); 94 | var state_file = state_files.shift().file; 95 | 96 | logger.trace('reading state file: ' + state_file); 97 | fs.readFile(matcher_state_dir + '/' + state_file, 'utf8', Chain.next()); 98 | }, 99 | function(err, data) { 100 | if (err) { 101 | logger.panic('could not read matcher state file', err); 102 | 103 | // TODO this will not be good if we have multiple matchers in one process 104 | process.exit(1); 105 | } 106 | 107 | var state = JSON.parse(data); 108 | 109 | // fill up the order book 110 | state.bids.forEach(function(order_data) { 111 | order_data.side = 0; 112 | var order = Order.parse(order_data); 113 | //logger.trace('adding back ' + util.inspect(order)); 114 | self.order_book.add(order); 115 | }); 116 | state.asks.forEach(function(order_data) { 117 | order_data.side = 1; 118 | var order = Order.parse(order_data); 119 | //logger.trace('adding back ' + util.inspect(order)); 120 | self.order_book.add(order); 121 | }); 122 | 123 | // set the other stateful fields 124 | self.state_num = state.state_num; 125 | self.output_seq = state.output_seq; 126 | self.ticker = state.ticker; 127 | 128 | // the readback filename is stored with the state 129 | // this will be the filename of the journal file the state was written to 130 | // this is not good because we need to recover from the _next_ file 131 | // after a rotation, it should be the file we need to read from 132 | // to do that tho, we have to pass the argument to the shits 133 | 134 | var journal_filename = state.recovery_journal; 135 | 136 | logger.trace('adding back data from state file, reading journal: ' + journal_filename); 137 | 138 | var base = path.basename(journal_filename, '.log'); 139 | 140 | // playback from the incoming logs 141 | var fstream = fs.createReadStream(env.journaldir + journal_filename); 142 | fstream.on('error', function(err) { 143 | logger.panic('could not read matcher journal', err); 144 | process.exit(1); 145 | }); 146 | 147 | var cstream = carrier.carry(fstream); 148 | 149 | // register the event handlers here because messages need to be 150 | // sent out during replay, but not before that! 151 | register_event_handlers(); 152 | 153 | // the state num in the journal (1 smaller than the saved one) 154 | var journal_state_num = self.state_num - 1; 155 | 156 | // do the replay 157 | var playback = false; 158 | cstream.on('line', function(line) { 159 | if (line.length) { 160 | var data = JSON.parse(line); 161 | if (data.type === "state" && data.payload === journal_state_num) { 162 | playback = true; 163 | return; 164 | } 165 | 166 | if (playback) { 167 | //logger.trace('playing back ' + util.inspect(data)); 168 | var handler = self._get_handler(data, send_feed_msg); 169 | if (!handler) { 170 | logger.warn('no handler for message type ' + data.type); 171 | return; 172 | } 173 | handler(data.payload); 174 | } 175 | } 176 | }); 177 | 178 | cstream.on('end', function() { 179 | // serious error, we have the wrong journal or it's corrupt 180 | if (!playback) { 181 | var errmsg = 'Could not find state num ' + 182 | journal_state_num + ' in journal'; 183 | logger.panic(errmsg, new Error(errmsg)); 184 | process.exit(1); 185 | } 186 | 187 | logger.trace('state recovery successful'); 188 | 189 | if (cb) 190 | cb(); 191 | }); 192 | } 193 | ); 194 | }; 195 | 196 | 197 | /// returns a handler that affects the matcher's state 198 | /// send_feed_msg is a function that will send a msg out 199 | /// ev is an optional event emitter, events will not be emitted if omitted 200 | Matcher.prototype._get_handler = function(msg, send_feed_msg, ev) { 201 | var self = this; 202 | // handlers for messages which will affect the matcher state 203 | var msg_handlers = { 204 | 'order': function(payload) { 205 | var order = Order.parse(payload); 206 | 207 | // received order into the matcher 208 | // this order status is sent to indicate that the order was received 209 | var payload = { 210 | status: 'received', 211 | side: order.side, 212 | order_id: order.id, 213 | user_id: order.user_id, 214 | price: order.price, 215 | size: order.size, 216 | fee_rate: payload.fee_rate, 217 | timestamp: time.timestamp() 218 | }; 219 | send_feed_msg('order_status', payload); 220 | 221 | // add the order to the order book 222 | // if the order can be matched immediately, it will be 223 | // this should happen after the sends because it may cause fills to be sent 224 | self.order_book.add(order); 225 | 226 | // for testing only 227 | self.emit('process'); 228 | }, 229 | 'cancel': function(payload) { 230 | var oid = payload.order_id; 231 | var user_id = payload.user_id; 232 | 233 | var result = self.order_book.remove(oid, user_id); 234 | 235 | // if there was an error, inform the user 236 | if (result && ev) { 237 | ev.emit('reply', { 238 | type: 'cancel_reject', 239 | timestamp: time.timestamp(), 240 | target_id: user_id, 241 | product_id: self.product_id, 242 | payload: { 243 | order_id: oid, 244 | reject_reason: result.message, 245 | } 246 | }); 247 | } 248 | 249 | // for testing only 250 | self.emit('process'); 251 | }, 252 | }; 253 | 254 | return msg_handlers[msg.type]; 255 | }; 256 | 257 | /// start the matcher, callback when started 258 | Matcher.prototype.start = function(cb) { 259 | var self = this; 260 | logger.trace('starting matcher'); 261 | 262 | var client = self.config.client; 263 | var feed = self.config.feed; 264 | 265 | var order_book = self.order_book; 266 | 267 | // the multicast feed channel socket 268 | var feed_socket = this.feed_socket = dgram.createSocket('udp4'); 269 | 270 | var ev = new events.EventEmitter(); 271 | 272 | // inbound message journal, initialized later 273 | self.journal_in = undefined; 274 | 275 | // output journal for the matcher 276 | // based on date 277 | var date = Math.floor(time.timestamp()); 278 | self.journal_out = new Journal('matcher_out.' + self.product_id + '.' + date, false); 279 | 280 | // rotate the outgoing journal 281 | // this prevents it from growing too large 282 | setInterval(function rotate_out_journal() { 283 | 284 | var old_journal_out = self.journal_out; 285 | 286 | var date = Math.floor(time.timestamp()); 287 | self.journal_out = new Journal('matcher_out.' + self.product_id + '.' + date, false); 288 | 289 | old_journal_out.log({ type: 'eof', next: self.journal_out.filename }); 290 | old_journal_out.close(); 291 | delete old_journal_out; 292 | }, journal_rotate_interval); 293 | 294 | // journal & send a message out on the multicast socket 295 | // updaters and other interested sources listen for this data 296 | function send_feed_msg(type, payload) { 297 | // avoid referencing into the feed config object for every message send 298 | var feed_ip = feed.ip; 299 | var feed_port = feed.port; 300 | 301 | // construct the message 302 | var msg = { 303 | type: type, 304 | timestamp: time.timestamp(), 305 | product_id: self.product_id, 306 | seq: self.output_seq, 307 | payload: payload 308 | }; 309 | 310 | // journal the message before sending it 311 | // it's not necessary to wait for this to finish, since 312 | // this is just for nightly reconciliation 313 | // state is persisted by the input journal & state files 314 | self.journal_out.log(msg); 315 | 316 | // have to send buffers 317 | var buff = new Buffer(JSON.stringify(msg)); 318 | 319 | // beam me up scotty! 320 | feed_socket.send(buff, 0, buff.length, feed_port, feed_ip, function(err) { 321 | if (err) 322 | logger.warn(err.message); 323 | }); 324 | 325 | ++self.output_seq; 326 | } 327 | 328 | function register_event_handlers() { 329 | /// order book event handlers 330 | order_book 331 | .on('add_order', function(order) { 332 | // client has already been notofied that the order is open at the exchange 333 | // we can't do it here because the order may never be added to the book if it is 334 | // executed immediately, thus no call to event dist 335 | // the 'open' order status means that the order is now open on the order book 336 | var payload = { 337 | status: 'open', 338 | side: order.side, 339 | order_id: order.id, 340 | user_id: order.user_id, 341 | price: order.price, 342 | size: order.size, 343 | timestamp: time.timestamp(), 344 | }; 345 | 346 | send_feed_msg('order_status', payload); 347 | }) 348 | // taker is the liq. taker, provider is the liq. provider 349 | .on('match', function(size, taker, provider) { 350 | var price = provider.price; 351 | var timestamp = time.timestamp(); 352 | var payload = { 353 | id: uuid('binary').toString('hex'), 354 | taker_id: taker.id, 355 | provider_id: provider.id, 356 | taker_user_id: taker.user_id, 357 | provider_user_id: provider.user_id, 358 | size: size, 359 | price: price, 360 | provider_side: provider.side, 361 | timestamp: timestamp, 362 | }; 363 | 364 | // save ticker info 365 | self.ticker = { 366 | price: price, 367 | size: size, 368 | timestamp: timestamp 369 | }; 370 | 371 | send_feed_msg('match', payload); 372 | }) 373 | .on('order_done', function(order) { 374 | var payload = { 375 | order_id: order.id, 376 | status: 'done', 377 | size: order.size, // need for fast cancel (hold amount calc) 378 | user_id: order.user_id, 379 | reason: (order.done) ? 'filled' : 'cancelled', 380 | timestamp: time.timestamp(), 381 | }; 382 | send_feed_msg('order_status', payload); 383 | }); 384 | 385 | } 386 | 387 | function get_in_filename() { 388 | //var date = Math.floor(Date.now()/1000); 389 | var date = Math.floor(time.timestamp()); 390 | return 'matcher_in.' + self.product_id + '.' + date; 391 | } 392 | 393 | // rotate the incoming journal to avoid it growing too large 394 | setInterval(function rotate_in_journal() { 395 | 396 | // we will need to close the old file 397 | var old_in_log = self.journal_in; 398 | 399 | // make the new file 400 | var filename = get_in_filename(); 401 | var new_in_log = new Journal(filename, false); 402 | 403 | self.journal_in = new_in_log; 404 | 405 | // write a state entry to the new log 406 | // this will mark the start of the file 407 | self.write_state(); 408 | 409 | old_in_log.log({type: 'eof', next: new_in_log.filename}, function(err) { 410 | old_in_log.close(); 411 | delete old_in_log; 412 | }); 413 | 414 | // a more robust solution would be to read in the statefile after it is written 415 | // if the statefile is "valid" not sure what that means yet.. maybe checksumed 416 | // then we know we can rotate the in journal because we can recover from the statefile 417 | // which means we don't need to read from the old file 418 | }, journal_rotate_interval); 419 | 420 | // matcher server setup and connection handling 421 | var server = this.server = net.createServer(); 422 | 423 | function start_server(err) { 424 | if (err) { 425 | logger.error(err); 426 | } 427 | 428 | // create a new incoming journal file 429 | self.journal_in = new Journal(get_in_filename(), false); 430 | 431 | // write state to file to make recovery cases easier 432 | self.write_state(function() { 433 | server.listen(client.port, client.ip, function() { 434 | logger.trace('matcher started'); 435 | 436 | // write state periodically 437 | function write_func() { 438 | self.write_state(); 439 | self.write_state_tid = setTimeout(write_func, write_state_period); 440 | } 441 | self.write_state_tid = setTimeout(write_func, write_state_period); 442 | 443 | if (cb) 444 | cb(); 445 | }); 446 | }); 447 | } 448 | 449 | if (self.config.no_recover) { 450 | register_event_handlers(); 451 | start_server(); 452 | } else { 453 | // recover the state before accepting requests 454 | self.recover(send_feed_msg, register_event_handlers, start_server); 455 | } 456 | 457 | // server event handlers 458 | server.on('connection', function(socket) { 459 | var addr = socket.remoteAddress + ":" + socket.remotePort; 460 | logger.trace('accepted connection from: ' + addr); 461 | 462 | // the outgoing messenger for the client 463 | var ms = new Messenger(socket); 464 | 465 | function send_reply(obj) { 466 | ms.send(obj); 467 | } 468 | 469 | ev.on('reply', send_reply); 470 | 471 | socket.on('close', function() { 472 | logger.trace('removing send_reply handler for ' + addr); 473 | ev.removeListener('reply', send_reply); 474 | }); 475 | 476 | ms.addListener('msg', function(msg) { 477 | var handler = self._get_handler(msg, send_feed_msg, ev); 478 | 479 | if (!handler) { 480 | // state requests don't happen often so only try to handle them 481 | // if we don't already have a handler for the message type 482 | // these are special messages not intended for the matcher 483 | if (msg.type === 'state') { 484 | self.write_state(function(state) { 485 | ms.send({ type: 'state', payload: state }); 486 | }); 487 | return; 488 | } 489 | 490 | // if we didn't have a handler and it wasn't a sub request 491 | return logger.warn('no handler for message type: ' + msg.type, msg); 492 | } 493 | 494 | // wait for journal write before processing request 495 | // these journaled messages affect the state of the matcher 496 | self.journal_in.log(msg, function() { 497 | if (!msg.payload) 498 | return logger.warn('no payload in message', msg); 499 | return handler(msg.payload); 500 | }); 501 | }); 502 | }); 503 | }; 504 | 505 | Matcher.prototype.stop = function(cb) { 506 | var self = this; 507 | logger.trace('stopping matcher'); 508 | 509 | // stop periodic state writes 510 | clearTimeout(self.write_state_tid); 511 | 512 | self.order_book.removeAllListeners(); 513 | 514 | self.server.on('close', function() { 515 | logger.trace('matcher stopped'); 516 | 517 | if(cb) 518 | cb(); 519 | }); 520 | 521 | self.server.close(); 522 | self.feed_socket.close(); 523 | }; 524 | 525 | /// stops the matcher and writes its state out for quick restart 526 | Matcher.prototype.shutdown = function(cb) { 527 | var self = this; 528 | this.stop(function() { 529 | self.write_state(cb); 530 | }); 531 | }; 532 | 533 | /// serializes and returns Matcher's state 534 | Matcher.prototype.state = function() { 535 | var state = this.order_book.state(); 536 | state.state_num = this.state_num; 537 | state.output_seq = this.output_seq; 538 | state.ticker = this.ticker; 539 | state.recovery_journal = path.basename(this.journal_in.filename); 540 | return state; 541 | }; 542 | 543 | /// writes the state to the state file 544 | /// cb(state) when done 545 | Matcher.prototype.write_state = function(cb) { 546 | var self = this; 547 | 548 | function state_filename(state_num) { 549 | return matcher_state_prefix + "." + self.product_id + "." + state_num + ".json"; 550 | } 551 | 552 | var state_num = self.state_num; 553 | ++self.state_num; 554 | 555 | self.journal_in.log({type: 'state', payload: state_num}, function() { 556 | var state = self.state(); 557 | 558 | // write the state to the outfile 559 | fs.writeFile(state_filename(state_num), JSON.stringify(state), function(err) { 560 | if(err) { 561 | logger.error('could not write to state file: ' + filename, err); 562 | } else { 563 | // remove old state files, leaving 2 most recent ones 564 | fs.unlink(state_filename(state_num-2)); 565 | } 566 | }); 567 | 568 | if(cb) { 569 | cb(state); 570 | } 571 | }); 572 | }; 573 | 574 | // resets matcher's state 575 | Matcher.prototype.reset = function() { 576 | this.output_seq = 1; 577 | this.state_num = 0; 578 | this.order_book = new OrderBook(); 579 | this.ticker = {}; 580 | }; 581 | 582 | module.exports = Matcher; 583 | -------------------------------------------------------------------------------- /lib/order.js: -------------------------------------------------------------------------------- 1 | 2 | // TODO: standardize the names used here 3 | function Order(id, price, size, side, user_id) { 4 | this.id = id; 5 | this.price = price; 6 | this.size = size; 7 | this.side = side; 8 | this.user_id = user_id; 9 | } 10 | 11 | Order.parse = function(order_dict) { 12 | return new Order(order_dict.order_id, 13 | order_dict.price, 14 | order_dict.size, 15 | order_dict.side, 16 | order_dict.user_id); 17 | } 18 | 19 | Order.prototype.toString = function() { 20 | return "" + this.size + "@" + this.price; 21 | } 22 | 23 | Order.prototype.state = function() { 24 | return { 25 | "price": this.price, 26 | "size": this.size, 27 | "order_id": this.id, 28 | "user_id": this.user_id 29 | }; 30 | } 31 | module.exports = Order; 32 | -------------------------------------------------------------------------------- /lib/order_book.js: -------------------------------------------------------------------------------- 1 | // builtin 2 | var events = require('events'); 3 | 4 | // common 5 | var logger = require('bitfloor/logger'); 6 | 7 | var Tree = require('bintrees').RBTree; 8 | 9 | // keeps a list of orders at a given price level 10 | function BookLevel(price) { 11 | this.price = price; 12 | this.orders = []; 13 | } 14 | 15 | // add an order to the level 16 | BookLevel.prototype.add = function(order) { 17 | this.orders.push(order); 18 | }; 19 | 20 | // remove an order from the level 21 | BookLevel.prototype.remove = function(order) { 22 | var orders = this.orders 23 | var index = orders.indexOf(order); 24 | orders.splice(index, 1); 25 | }; 26 | 27 | function OrderBook() { 28 | this._bid = new Tree(function(a, b) { return a.price - b.price; }); 29 | this._ask = new Tree(function(a, b) { return a.price - b.price; }); 30 | this._by_oid = {}; 31 | } 32 | 33 | // the order book emits level_update events 34 | // level_update(side, price, size) 35 | OrderBook.prototype = new events.EventEmitter(); 36 | 37 | /// return the order tree for the given book side 38 | /// bid: 0, ask: 1 39 | OrderBook.prototype._get_tree = function(side) { 40 | return side == 0 ? this._bid : this._ask; 41 | } 42 | 43 | /// insert an order into the order book 44 | OrderBook.prototype._insert = function(order) { 45 | var price = order.price; 46 | 47 | var tree = this._get_tree(order.side); 48 | 49 | var level = tree.find(order); 50 | 51 | // no existing level, make new 52 | if(!level) { 53 | level = new BookLevel(price); 54 | tree.insert(level); 55 | } 56 | 57 | // add order to level and order id map 58 | level.add(order); 59 | this._by_oid[order.id] = order; 60 | 61 | this.emit('add_order', order); 62 | 63 | // TODO handle case where order could not be added for some reason 64 | } 65 | 66 | /// delete an order from the order book 67 | OrderBook.prototype._delete = function(order) { 68 | delete this._by_oid[order.id]; 69 | 70 | // get the side of the book for the order we want removed 71 | var tree = this._get_tree(order.side); 72 | 73 | // get the price level orders 74 | var level = tree.find(order); 75 | var orders = level.orders; 76 | 77 | if(level.orders.length == 1) { 78 | // we are the only order in the array 79 | // can remove the whole level 80 | tree.remove(level); 81 | } else { 82 | level.remove(order); 83 | } 84 | 85 | this.emit('order_done', order); 86 | } 87 | 88 | /* 89 | * removes order by oid if it exists and it has the correct user_id 90 | */ 91 | OrderBook.prototype.remove = function(oid, user_id) { 92 | var order = this._by_oid[oid]; 93 | if(!order) 94 | return new Error('not found'); 95 | 96 | if(order.user_id != user_id) 97 | return new Error('invalid user_id'); 98 | 99 | // ok to remove the order 100 | this._delete(order); 101 | } 102 | 103 | /// attempt to add the order to the order book 104 | /// will match the order first before trying to add it 105 | OrderBook.prototype.add = function(order) { 106 | while(this._match_one(order)) {} 107 | 108 | if (order.size < 0) { 109 | logger.panic('order size < 0 after matching (oid): ' + order.id, order); 110 | return; 111 | } else if(order.size == 0) { 112 | // order was fully consumed 113 | this.emit('order_done', order); 114 | return; 115 | } 116 | 117 | // if the order was not fully matched, then it will be put into the order book 118 | this._insert(order); 119 | } 120 | 121 | /// match the order against an existing orders in the order book 122 | /// only match against the first available order 123 | /// returns false if there can't be any more matches 124 | /// either due to price being too low or no remaining orders 125 | OrderBook.prototype._match_one = function(order) { 126 | 127 | // the complementary order for the one we want to match 128 | var best; 129 | 130 | if(order.side == 0) { 131 | // order is bid, need the best ask 132 | var level = this._ask.min(); 133 | if(!level) 134 | return false; 135 | 136 | best = level.orders[0]; 137 | 138 | // for a bid, the best price should be <= the order price 139 | // if the best price > order price, then we can't match 140 | if(best.price > order.price) 141 | return false; 142 | } else if(order.side == 1) { 143 | // order is ask, need the best bid 144 | var level = this._bid.max(); 145 | if(!level) 146 | return false; 147 | 148 | best = level.orders[0]; 149 | 150 | // for an ask, the best price should be >= order price 151 | // if the best price < order price, then we can't match 152 | if(best.price < order.price) 153 | return false; 154 | } else { 155 | assert(false); 156 | } 157 | 158 | if (!best) 159 | return false; 160 | 161 | // prevent trading with self 162 | if(best.user_id === order.user_id) { 163 | // delete the order on the book, because it is older 164 | this._delete(best); 165 | 166 | // continue eating through other orders, if available 167 | return true; 168 | } 169 | 170 | // size is the smaller of the two complimentary orders 171 | var size = Math.min(order.size, best.size); 172 | order.size -= size; 173 | best.size -= size; 174 | 175 | // mark the orders as done if they are fully exhaused 176 | // match will use this information 177 | if (best.size == 0) 178 | best.done = true; 179 | 180 | if (order.size == 0) 181 | order.done = true; 182 | 183 | // the match happens at best.price 184 | // send out a single match event for the whole match 185 | // this will send out a done event for each order if it was completed 186 | // the best order has already been removed at this point if it was done 187 | 188 | this.emit('match', size, order, best); 189 | 190 | // if best was fully consumed, then it can be removed from the book 191 | // this should happen after the match because it will cause a done message to occur 192 | // only best needs to be deleted because order was not added yet 193 | if (best.done) 194 | this._delete(best); 195 | 196 | // will cause the order to be added to the order book 197 | return order.size > 0; 198 | } 199 | 200 | OrderBook.prototype.state = function() { 201 | var bids = []; 202 | var asks = []; 203 | 204 | this._bid.reach(function(e) { 205 | e.orders.forEach(function(o) { 206 | bids.push(o.state()); 207 | }); 208 | }); 209 | 210 | this._ask.each(function(e) { 211 | e.orders.forEach(function(o) { 212 | asks.push(o.state()); 213 | }); 214 | }); 215 | 216 | return { 217 | bids: bids, 218 | asks: asks 219 | }; 220 | } 221 | 222 | exports.OrderBook = OrderBook; 223 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "bitfloor", 3 | "name": "bitfloor_matching_engine", 4 | "version": "0.0.1", 5 | "homepage": "bitfloor.com", 6 | "repository": { 7 | "type": "git" 8 | , "url": "https://github.com/bitfloor/matching-engine.git" 9 | }, 10 | "dependencies": { 11 | "bintrees": "0.0.4" 12 | , "node-uuid": "1.2.0" 13 | , "nopt": "1.0.10" 14 | , "carrier": "0.1.3" 15 | , "chain": "https://github.com/shtylman/chain-js/tarball/master" 16 | }, 17 | "scripts": { 18 | "test": "nodeunit test" 19 | }, 20 | "devDependencies": { 21 | "nodeunit": "*" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/get_state.js: -------------------------------------------------------------------------------- 1 | var net = require('net'); 2 | var util = require('util'); 3 | 4 | var Messenger = require('bitfloor/messenger'); 5 | 6 | var config = require('bitfloor/config').get_matcher(process.argv[2]); 7 | 8 | var client = net.createConnection(config.client.port); 9 | client.on('connect', function() { 10 | var ms = new Messenger(client); 11 | 12 | ms.addListener('msg', function(msg) { 13 | console.log(util.inspect(msg, false, 100)); 14 | process.exit(); 15 | }); 16 | 17 | ms.send({type: 'state'}); 18 | }); 19 | -------------------------------------------------------------------------------- /test/recover.js: -------------------------------------------------------------------------------- 1 | // set timestamps to be fake timestamps 2 | require('bitfloor/time').use_test_timestamps(); 3 | 4 | var net = require('net'); 5 | var fs = require('fs'); 6 | var dgram = require('dgram'); 7 | var exec = require('child_process').exec; 8 | 9 | var logger = require('bitfloor/logger'); 10 | var Messenger = require('bitfloor/messenger'); 11 | 12 | var Matcher = require('../'); 13 | var json2 = require('../deps/json2'); // for pretty-serialize 14 | 15 | var BASE_DIR = __dirname + "/recover"; 16 | var TIMEOUT = 100; 17 | 18 | var env = require('bitfloor/config').env; 19 | 20 | logger.silence(0); 21 | 22 | var matcher_config = { 23 | client: { 24 | ip: 'localhost', 25 | port: 10001 26 | }, 27 | feed: { 28 | ip: '239.255.0.1', 29 | port: 10001 30 | } 31 | }; 32 | 33 | // create the matcher used for testing 34 | var product_id = 0; // fake id, matcher doesn't care except to save state 35 | var matcher = new Matcher(product_id, matcher_config); 36 | 37 | function clear_log_dir(cb) { 38 | exec("rm -rf " + env.logdir + "/*", function(error) { 39 | if (error) { 40 | console.log('ERROR:'); 41 | console.log(error); 42 | process.exit(1); 43 | } 44 | cb(); 45 | }); 46 | } 47 | 48 | function clear_state_dir(cb) { 49 | exec("rm -rf " + env.statedir + "/*", function(error) { 50 | if (error) { 51 | console.log('ERROR:'); 52 | console.log(error); 53 | process.exit(1); 54 | } 55 | cb(); 56 | }); 57 | } 58 | 59 | function clear_journal_dir(cb) { 60 | exec("rm -rf " + env.journaldir + "/*", function(error) { 61 | if (error) { 62 | console.log('ERROR:'); 63 | console.log(error); 64 | process.exit(1); 65 | } 66 | cb(); 67 | }); 68 | } 69 | 70 | function do_test(test_name, assert, cb) { 71 | clear_log_dir(function() { 72 | clear_state_dir(function() { 73 | clear_journal_dir(function() { 74 | // reset matcher state 75 | matcher.reset(); 76 | 77 | matcher.start(function() { 78 | run_test(test_name, assert, cb); 79 | }); 80 | }); 81 | }); 82 | }); 83 | } 84 | 85 | function run_test(test_name, assert, cb) { 86 | var test_file = BASE_DIR + "/" + test_name; 87 | var orders = JSON.parse(fs.readFileSync(test_file)); 88 | 89 | var client = net.createConnection(matcher_config.client.port); 90 | 91 | client.on('connect', function() { 92 | var ms = new Messenger(client); 93 | 94 | orders.forEach(function(order) { 95 | ms.send(order); 96 | }); 97 | 98 | // TODO: jenky 99 | setTimeout(function() { 100 | client.end(); 101 | run_recover(assert, cb); 102 | }, 100); 103 | }); 104 | } 105 | 106 | function run_recover(assert, cb) { 107 | var gold_state = matcher.state(); 108 | 109 | // increment state num because it is incremented when writing state to state file 110 | gold_state.state_num++; // TODO: jenky, because of a jenky thing in the matcher 111 | 112 | // stops the matcher and tries to have it startup again and read the old state 113 | matcher.stop(function() { 114 | matcher.reset(); 115 | matcher.start(function() { 116 | var state = matcher.state(); 117 | assert.deepEqual(state, gold_state); 118 | matcher.stop(cb); 119 | }); 120 | }); 121 | } 122 | 123 | function process_tests(tests) { 124 | function make_test(name) { 125 | return function(assert) { 126 | do_test(name, assert, function(ret) { 127 | assert.done(); 128 | }); 129 | } 130 | } 131 | 132 | var test_name; 133 | while (test_name = tests.shift()) { 134 | // skip hidden files 135 | if(test_name[0] === '.') { 136 | continue; 137 | } 138 | 139 | module.exports[test_name] = make_test(test_name); 140 | } 141 | } 142 | 143 | var tests = fs.readdirSync(BASE_DIR); 144 | process_tests(tests); 145 | -------------------------------------------------------------------------------- /test/recover/end.js: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794516561, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": 2, 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794516561, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": 2, 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794516561, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": 2, 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794516562, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1800000000, 41 | "user_id": 3, 42 | "side": 1, 43 | "size": 8056912305 44 | } 45 | }, 46 | { 47 | "timestamp": 1311794516562, 48 | "type": "order", 49 | "payload": { 50 | "order_id": 3, 51 | "price": 1100000000, 52 | "user_id": 3, 53 | "side": 1, 54 | "size": 120000000 55 | } 56 | }, 57 | { 58 | "timestamp": 1311794516562, 59 | "type": "order", 60 | "payload": { 61 | "order_id": 4, 62 | "price": 1100000000, 63 | "user_id": 3, 64 | "side": 1, 65 | "size": 210000000 66 | } 67 | }, 68 | { 69 | "timestamp": 1311794516562, 70 | "type": "order", 71 | "payload": { 72 | "order_id": 7, 73 | "price": 1100000000, 74 | "user_id": 2, 75 | "side": 0, 76 | "size": 120000000 77 | } 78 | }, 79 | { 80 | "timestamp": 1311794516562, 81 | "type": "order", 82 | "payload": { 83 | "order_id": 9, 84 | "price": 1024000000, 85 | "user_id": 3, 86 | "side": 1, 87 | "size": 1023400000 88 | } 89 | }, 90 | { 91 | "timestamp": 1311794516562, 92 | "type": "order", 93 | "payload": { 94 | "order_id": 10, 95 | "price": 1100000000, 96 | "user_id": 2, 97 | "side": 0, 98 | "size": 210000000 99 | } 100 | }, 101 | { 102 | "type": "state" 103 | } 104 | ] 105 | -------------------------------------------------------------------------------- /test/recover/multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state" 4 | }, 5 | { 6 | "timestamp": 1311794516561, 7 | "type": "order", 8 | "payload": { 9 | "order_id": 0, 10 | "price": 1023000000, 11 | "user_id": 2, 12 | "side": 0, 13 | "size": 5234591011 14 | } 15 | }, 16 | { 17 | "timestamp": 1311794516561, 18 | "type": "order", 19 | "payload": { 20 | "order_id": 1, 21 | "price": 1024000000, 22 | "user_id": 2, 23 | "side": 0, 24 | "size": 1023400000 25 | } 26 | }, 27 | { 28 | "timestamp": 1311794516561, 29 | "type": "order", 30 | "payload": { 31 | "order_id": 2, 32 | "price": 1023000000, 33 | "user_id": 2, 34 | "side": 0, 35 | "size": 1020000000 36 | } 37 | }, 38 | { 39 | "type": "state" 40 | }, 41 | { 42 | "timestamp": 1311794516562, 43 | "type": "order", 44 | "payload": { 45 | "order_id": 5, 46 | "price": 1800000000, 47 | "user_id": 3, 48 | "side": 1, 49 | "size": 8056912305 50 | } 51 | }, 52 | { 53 | "timestamp": 1311794516562, 54 | "type": "order", 55 | "payload": { 56 | "order_id": 3, 57 | "price": 1100000000, 58 | "user_id": 3, 59 | "side": 1, 60 | "size": 120000000 61 | } 62 | }, 63 | { 64 | "type": "state" 65 | }, 66 | { 67 | "timestamp": 1311794516562, 68 | "type": "order", 69 | "payload": { 70 | "order_id": 4, 71 | "price": 1100000000, 72 | "user_id": 3, 73 | "side": 1, 74 | "size": 210000000 75 | } 76 | }, 77 | { 78 | "timestamp": 1311794516562, 79 | "type": "order", 80 | "payload": { 81 | "order_id": 7, 82 | "price": 1100000000, 83 | "user_id": 2, 84 | "side": 0, 85 | "size": 120000000 86 | } 87 | }, 88 | { 89 | "timestamp": 1311794516562, 90 | "type": "order", 91 | "payload": { 92 | "order_id": 9, 93 | "price": 1024000000, 94 | "user_id": 3, 95 | "side": 1, 96 | "size": 1023400000 97 | } 98 | }, 99 | { 100 | "timestamp": 1311794516562, 101 | "type": "order", 102 | "payload": { 103 | "order_id": 10, 104 | "price": 1100000000, 105 | "user_id": 2, 106 | "side": 0, 107 | "size": 210000000 108 | } 109 | } 110 | ] 111 | -------------------------------------------------------------------------------- /test/recover/none.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794516561, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": 2, 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794516561, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": 2, 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794516561, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": 2, 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794516562, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1800000000, 41 | "user_id": 3, 42 | "side": 1, 43 | "size": 8056912305 44 | } 45 | }, 46 | { 47 | "timestamp": 1311794516562, 48 | "type": "order", 49 | "payload": { 50 | "order_id": 3, 51 | "price": 1100000000, 52 | "user_id": 3, 53 | "side": 1, 54 | "size": 120000000 55 | } 56 | }, 57 | { 58 | "timestamp": 1311794516562, 59 | "type": "order", 60 | "payload": { 61 | "order_id": 4, 62 | "price": 1100000000, 63 | "user_id": 3, 64 | "side": 1, 65 | "size": 210000000 66 | } 67 | }, 68 | { 69 | "timestamp": 1311794516562, 70 | "type": "order", 71 | "payload": { 72 | "order_id": 7, 73 | "price": 1100000000, 74 | "user_id": 2, 75 | "side": 0, 76 | "size": 120000000 77 | } 78 | }, 79 | { 80 | "timestamp": 1311794516562, 81 | "type": "order", 82 | "payload": { 83 | "order_id": 9, 84 | "price": 1024000000, 85 | "user_id": 3, 86 | "side": 1, 87 | "size": 1023400000 88 | } 89 | }, 90 | { 91 | "timestamp": 1311794516562, 92 | "type": "order", 93 | "payload": { 94 | "order_id": 10, 95 | "price": 1100000000, 96 | "user_id": 2, 97 | "side": 0, 98 | "size": 210000000 99 | } 100 | } 101 | ] 102 | -------------------------------------------------------------------------------- /test/recover/start.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state" 4 | }, 5 | { 6 | "timestamp": 1311794516561, 7 | "type": "order", 8 | "payload": { 9 | "order_id": 0, 10 | "price": 1023000000, 11 | "user_id": 2, 12 | "side": 0, 13 | "size": 5234591011 14 | } 15 | }, 16 | { 17 | "timestamp": 1311794516561, 18 | "type": "order", 19 | "payload": { 20 | "order_id": 1, 21 | "price": 1024000000, 22 | "user_id": 2, 23 | "side": 0, 24 | "size": 1023400000 25 | } 26 | }, 27 | { 28 | "timestamp": 1311794516561, 29 | "type": "order", 30 | "payload": { 31 | "order_id": 2, 32 | "price": 1023000000, 33 | "user_id": 2, 34 | "side": 0, 35 | "size": 1020000000 36 | } 37 | }, 38 | { 39 | "timestamp": 1311794516562, 40 | "type": "order", 41 | "payload": { 42 | "order_id": 5, 43 | "price": 1800000000, 44 | "user_id": 3, 45 | "side": 1, 46 | "size": 8056912305 47 | } 48 | }, 49 | { 50 | "timestamp": 1311794516562, 51 | "type": "order", 52 | "payload": { 53 | "order_id": 3, 54 | "price": 1100000000, 55 | "user_id": 3, 56 | "side": 1, 57 | "size": 120000000 58 | } 59 | }, 60 | { 61 | "timestamp": 1311794516562, 62 | "type": "order", 63 | "payload": { 64 | "order_id": 4, 65 | "price": 1100000000, 66 | "user_id": 3, 67 | "side": 1, 68 | "size": 210000000 69 | } 70 | }, 71 | { 72 | "timestamp": 1311794516562, 73 | "type": "order", 74 | "payload": { 75 | "order_id": 7, 76 | "price": 1100000000, 77 | "user_id": 2, 78 | "side": 0, 79 | "size": 120000000 80 | } 81 | }, 82 | { 83 | "timestamp": 1311794516562, 84 | "type": "order", 85 | "payload": { 86 | "order_id": 9, 87 | "price": 1024000000, 88 | "user_id": 3, 89 | "side": 1, 90 | "size": 1023400000 91 | } 92 | }, 93 | { 94 | "timestamp": 1311794516562, 95 | "type": "order", 96 | "payload": { 97 | "order_id": 10, 98 | "price": 1100000000, 99 | "user_id": 2, 100 | "side": 0, 101 | "size": 210000000 102 | } 103 | } 104 | ] 105 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | to regenerate gold files: 3 | node test.js -g 4 | */ 5 | 6 | // set timestamps to be fake timestamps 7 | require('bitfloor/time').use_test_timestamps(); 8 | 9 | var net = require('net'); 10 | var fs = require('fs'); 11 | var dgram = require('dgram'); 12 | 13 | var _ = require('underscore'); 14 | 15 | var logger = require('bitfloor/logger'); 16 | var Messenger = require('bitfloor/messenger'); 17 | 18 | var Matcher = require('../'); 19 | var json2 = require('../deps/json2'); // for pretty-serialize 20 | 21 | var BASE_DIR = __dirname + "/unit"; 22 | var TIMEOUT = 100; 23 | 24 | var matcher_config = { 25 | no_recover: true, 26 | client: { 27 | ip: 'localhost', 28 | port: 10001 29 | }, 30 | feed: { 31 | ip: '239.255.0.1', 32 | port: 10001 33 | } 34 | }; 35 | 36 | // create the matcher used for testing 37 | var product_id = 0; // fake id, matcher doesn't care except to save state 38 | var matcher = new Matcher(product_id, matcher_config); 39 | 40 | var env = require('bitfloor/config').env; 41 | 42 | var journal_file = env.journaldir + '/matcher_in.0.1317086239.log'; 43 | var journal_file_out = env.journaldir + '/matcher_out.0.1317086239.log'; 44 | 45 | var gen_golds = process.argv.length > 2 && process.argv[2] == '-g'; 46 | 47 | function do_test(test_name, assert, cb) { 48 | // clear the journals 49 | fs.writeFileSync(journal_file, ""); 50 | fs.writeFileSync(journal_file_out, ""); 51 | 52 | // reset matcher state 53 | matcher.reset(); 54 | 55 | matcher.start(function() { 56 | run_test(test_name, assert, cb); 57 | }); 58 | } 59 | 60 | function run_test(test_name, assert, cb) { 61 | var test_dir = BASE_DIR + "/" + test_name; 62 | var journal_test_file = test_dir + "/journal.log"; 63 | var recv_filename = test_dir + "/recv.json"; 64 | var recvm_filename = test_dir + "/recv_multi.json"; 65 | var state_filename = test_dir + "/state.json"; 66 | var orders = JSON.parse(fs.readFileSync(test_dir + "/send.json")); 67 | var start_time; 68 | var end_time; 69 | 70 | var tid; // timeout id 71 | 72 | var resps = []; 73 | var resps_multi = []; 74 | var states = []; 75 | 76 | var feed = dgram.createSocket('udp4'); 77 | 78 | var client = net.createConnection(matcher_config.client.port); 79 | client.on('connect', function() { 80 | var ms = new Messenger(client); 81 | 82 | // listen for multicast messages 83 | feed.bind(matcher_config.feed.port, matcher_config.feed.ip); 84 | feed.addMembership(matcher_config.feed.ip); 85 | 86 | ms.addListener('msg', function(msg) { 87 | end_time = Date.now()/1000; 88 | resps.push(msg); 89 | 90 | clearTimeout(tid); 91 | tid = setTimeout(end, TIMEOUT); 92 | }); 93 | 94 | feed.on('message', function(msg_buf) { 95 | end_time = Date.now()/1000; 96 | 97 | var msg = JSON.parse(msg_buf.toString('utf8')); 98 | resps_multi.push(msg); 99 | 100 | clearTimeout(tid); 101 | tid = setTimeout(end, TIMEOUT); 102 | }); 103 | 104 | start_time = Date.now()/1000; 105 | orders.forEach(function(order){ 106 | ms.send(order); 107 | }); 108 | 109 | // send a state message, just to test that code 110 | ms.send({type: 'state'}); 111 | 112 | logger.trace('sent all messages for: ' + test_name) 113 | 114 | tid = setTimeout(end, TIMEOUT); 115 | }); 116 | 117 | matcher.on('process', function() { 118 | states.push(matcher.state()); 119 | }); 120 | 121 | function process_journal(journal) { 122 | var a = []; 123 | journal.split('\n').forEach(function(line) { 124 | if(line.length) { 125 | var obj = JSON.parse(line); 126 | a.push(obj); 127 | } 128 | }); 129 | remove_match_ids(a); 130 | return a; 131 | } 132 | 133 | // remove all match ids 134 | // match ids need to be removed because they are randomly generated 135 | // and thus will not match during testing 136 | function remove_match_ids(arr) { 137 | arr.forEach(function(m) { 138 | if(m.type === 'match') 139 | delete m.payload.id; 140 | }); 141 | } 142 | 143 | function end() { 144 | feed.close(); 145 | client.end(); 146 | matcher.stop(function() { 147 | if(cb) { 148 | cb({ 149 | time: end_time - start_time 150 | }); 151 | } 152 | }); 153 | 154 | remove_match_ids(resps_multi); 155 | 156 | var journal = fs.readFileSync(journal_file) + ""; 157 | 158 | if(gen_golds) { 159 | fs.writeFileSync(journal_test_file, journal); 160 | fs.writeFileSync(recv_filename, json2.stringify(resps, null, '\t')); 161 | fs.writeFileSync(recvm_filename, json2.stringify(resps_multi, null, '\t')); 162 | fs.writeFileSync(state_filename, json2.stringify(states, null, '\t')); 163 | } 164 | else { 165 | var grecv = JSON.parse(fs.readFileSync(recv_filename)); 166 | var grecvm = JSON.parse(fs.readFileSync(recvm_filename)); 167 | var gstate = JSON.parse(fs.readFileSync(state_filename)); 168 | var gjournal = fs.readFileSync(journal_test_file) + ""; 169 | assert.deepEqual(resps, grecv); 170 | assert.deepEqual(resps_multi, grecvm); 171 | assert.deepEqual(states, gstate); 172 | assert.deepEqual(process_journal(journal), process_journal(gjournal)); 173 | } 174 | } 175 | } 176 | 177 | function make_test(name) { 178 | return function(assert) { 179 | do_test(name, assert, function(ret) { 180 | logger.trace('ran test in: ' + ret.time); 181 | assert.done(); 182 | }); 183 | } 184 | } 185 | 186 | // load and run tests 187 | var tests = fs.readdirSync(BASE_DIR); 188 | 189 | // if we ran as mainprog (not under nodeunit) then we need to run manually 190 | // TODO this is jenky and should be changed 191 | if (require.main === module) { 192 | var assert = require('assert'); 193 | 194 | function process_tests(tests) { 195 | var test = tests.shift(); 196 | if(test) { 197 | logger.trace('running test: ' + test); 198 | do_test(test, assert, function(ret) { 199 | console.log('ran test in', ret.time); 200 | process_tests(tests); 201 | }); 202 | } 203 | } 204 | 205 | process_tests(tests); 206 | } else { 207 | // running under nodeunit, setup tests 208 | 209 | logger.silence(0); 210 | var test_name; 211 | while (test_name = tests.shift()) { 212 | module.exports[test_name] = make_test(test_name); 213 | } 214 | } 215 | 216 | -------------------------------------------------------------------------------- /test/unit/add_cancel/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1023000000,"user_id":2,"side":0,"size":5234591011},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1024000000,"user_id":2,"side":0,"size":1023400000},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":2,"price":1023000000,"user_id":2,"side":0,"size":1020000000},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":0,"user_id":2},"seq":4} 6 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":5,"price":1800000000,"user_id":2,"side":1,"size":8056912305},"seq":5} 7 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":3,"price":1100000000,"user_id":2,"side":1,"size":120000000},"seq":6} 8 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":4,"price":1100000000,"user_id":2,"side":1,"size":210000000},"seq":7} 9 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":2,"user_id":2},"seq":8} 10 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":0,"user_id":2},"seq":9} 11 | {"type":"state","payload":1} 12 | -------------------------------------------------------------------------------- /test/unit/add_cancel/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "cancel_reject", 4 | "timestamp": 1317086239.693123, 5 | "target_id": 2, 6 | "product_id": 0, 7 | "payload": { 8 | "order_id": 0, 9 | "reject_reason": "not found" 10 | }, 11 | "seq": 1 12 | }, 13 | { 14 | "type": "state", 15 | "payload": { 16 | "bids": [ 17 | { 18 | "price": 1024000000, 19 | "size": 1023400000, 20 | "order_id": 1, 21 | "user_id": 2 22 | } 23 | ], 24 | "asks": [ 25 | { 26 | "price": 1100000000, 27 | "size": 120000000, 28 | "order_id": 3, 29 | "user_id": 2 30 | }, 31 | { 32 | "price": 1100000000, 33 | "size": 210000000, 34 | "order_id": 4, 35 | "user_id": 2 36 | }, 37 | { 38 | "price": 1800000000, 39 | "size": 8056912305, 40 | "order_id": 5, 41 | "user_id": 2 42 | } 43 | ], 44 | "state_num": 2, 45 | "output_seq": 15, 46 | "ticker": {}, 47 | "recovery_journal": "matcher_in.0.1317086239.log" 48 | }, 49 | "timestamp": 1317086239.693123, 50 | "seq": 2 51 | } 52 | ] -------------------------------------------------------------------------------- /test/unit/add_cancel/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": 2, 12 | "price": 1023000000, 13 | "size": 5234591011, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": 2, 27 | "price": 1023000000, 28 | "size": 5234591011, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 0, 40 | "order_id": 1, 41 | "user_id": 2, 42 | "price": 1024000000, 43 | "size": 1023400000, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "order_status", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "status": "open", 54 | "side": 0, 55 | "order_id": 1, 56 | "user_id": 2, 57 | "price": 1024000000, 58 | "size": 1023400000, 59 | "timestamp": 1317086239.693123 60 | } 61 | }, 62 | { 63 | "type": "order_status", 64 | "timestamp": 1317086239.693123, 65 | "product_id": 0, 66 | "seq": 5, 67 | "payload": { 68 | "status": "received", 69 | "side": 0, 70 | "order_id": 2, 71 | "user_id": 2, 72 | "price": 1023000000, 73 | "size": 1020000000, 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "status": "open", 84 | "side": 0, 85 | "order_id": 2, 86 | "user_id": 2, 87 | "price": 1023000000, 88 | "size": 1020000000, 89 | "timestamp": 1317086239.693123 90 | } 91 | }, 92 | { 93 | "type": "order_status", 94 | "timestamp": 1317086239.693123, 95 | "product_id": 0, 96 | "seq": 7, 97 | "payload": { 98 | "order_id": 0, 99 | "status": "done", 100 | "size": 5234591011, 101 | "user_id": 2, 102 | "reason": "cancelled", 103 | "timestamp": 1317086239.693123 104 | } 105 | }, 106 | { 107 | "type": "order_status", 108 | "timestamp": 1317086239.693123, 109 | "product_id": 0, 110 | "seq": 8, 111 | "payload": { 112 | "status": "received", 113 | "side": 1, 114 | "order_id": 5, 115 | "user_id": 2, 116 | "price": 1800000000, 117 | "size": 8056912305, 118 | "timestamp": 1317086239.693123 119 | } 120 | }, 121 | { 122 | "type": "order_status", 123 | "timestamp": 1317086239.693123, 124 | "product_id": 0, 125 | "seq": 9, 126 | "payload": { 127 | "status": "open", 128 | "side": 1, 129 | "order_id": 5, 130 | "user_id": 2, 131 | "price": 1800000000, 132 | "size": 8056912305, 133 | "timestamp": 1317086239.693123 134 | } 135 | }, 136 | { 137 | "type": "order_status", 138 | "timestamp": 1317086239.693123, 139 | "product_id": 0, 140 | "seq": 10, 141 | "payload": { 142 | "status": "received", 143 | "side": 1, 144 | "order_id": 3, 145 | "user_id": 2, 146 | "price": 1100000000, 147 | "size": 120000000, 148 | "timestamp": 1317086239.693123 149 | } 150 | }, 151 | { 152 | "type": "order_status", 153 | "timestamp": 1317086239.693123, 154 | "product_id": 0, 155 | "seq": 11, 156 | "payload": { 157 | "status": "open", 158 | "side": 1, 159 | "order_id": 3, 160 | "user_id": 2, 161 | "price": 1100000000, 162 | "size": 120000000, 163 | "timestamp": 1317086239.693123 164 | } 165 | }, 166 | { 167 | "type": "order_status", 168 | "timestamp": 1317086239.693123, 169 | "product_id": 0, 170 | "seq": 12, 171 | "payload": { 172 | "status": "received", 173 | "side": 1, 174 | "order_id": 4, 175 | "user_id": 2, 176 | "price": 1100000000, 177 | "size": 210000000, 178 | "timestamp": 1317086239.693123 179 | } 180 | }, 181 | { 182 | "type": "order_status", 183 | "timestamp": 1317086239.693123, 184 | "product_id": 0, 185 | "seq": 13, 186 | "payload": { 187 | "status": "open", 188 | "side": 1, 189 | "order_id": 4, 190 | "user_id": 2, 191 | "price": 1100000000, 192 | "size": 210000000, 193 | "timestamp": 1317086239.693123 194 | } 195 | }, 196 | { 197 | "type": "order_status", 198 | "timestamp": 1317086239.693123, 199 | "product_id": 0, 200 | "seq": 14, 201 | "payload": { 202 | "order_id": 2, 203 | "status": "done", 204 | "size": 1020000000, 205 | "user_id": 2, 206 | "reason": "cancelled", 207 | "timestamp": 1317086239.693123 208 | } 209 | } 210 | ] -------------------------------------------------------------------------------- /test/unit/add_cancel/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311636545134, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": 2, 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311636545134, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": 2, 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311636545134, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": 2, 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311636545134, 37 | "type": "cancel", 38 | "payload": { 39 | "order_id": 0, 40 | "user_id": 2 41 | } 42 | }, 43 | { 44 | "timestamp": 1311636545134, 45 | "type": "order", 46 | "payload": { 47 | "order_id": 5, 48 | "price": 1800000000, 49 | "user_id": 2, 50 | "side": 1, 51 | "size": 8056912305 52 | } 53 | }, 54 | { 55 | "timestamp": 1311636545135, 56 | "type": "order", 57 | "payload": { 58 | "order_id": 3, 59 | "price": 1100000000, 60 | "user_id": 2, 61 | "side": 1, 62 | "size": 120000000 63 | } 64 | }, 65 | { 66 | "timestamp": 1311636545135, 67 | "type": "order", 68 | "payload": { 69 | "order_id": 4, 70 | "price": 1100000000, 71 | "user_id": 2, 72 | "side": 1, 73 | "size": 210000000 74 | } 75 | }, 76 | { 77 | "timestamp": 1311636545135, 78 | "type": "cancel", 79 | "payload": { 80 | "order_id": 2, 81 | "user_id": 2 82 | } 83 | }, 84 | { 85 | "timestamp": 1311636545135, 86 | "type": "cancel", 87 | "payload": { 88 | "order_id": 0, 89 | "user_id": 2 90 | } 91 | } 92 | ] 93 | -------------------------------------------------------------------------------- /test/unit/add_cancel/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1023000000, 6 | "size": 5234591011, 7 | "order_id": 0, 8 | "user_id": 2 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1024000000, 21 | "size": 1023400000, 22 | "order_id": 1, 23 | "user_id": 2 24 | }, 25 | { 26 | "price": 1023000000, 27 | "size": 5234591011, 28 | "order_id": 0, 29 | "user_id": 2 30 | } 31 | ], 32 | "asks": [], 33 | "state_num": 2, 34 | "output_seq": 5, 35 | "ticker": {}, 36 | "recovery_journal": "matcher_in.0.1317086239.log" 37 | }, 38 | { 39 | "bids": [ 40 | { 41 | "price": 1024000000, 42 | "size": 1023400000, 43 | "order_id": 1, 44 | "user_id": 2 45 | }, 46 | { 47 | "price": 1023000000, 48 | "size": 5234591011, 49 | "order_id": 0, 50 | "user_id": 2 51 | }, 52 | { 53 | "price": 1023000000, 54 | "size": 1020000000, 55 | "order_id": 2, 56 | "user_id": 2 57 | } 58 | ], 59 | "asks": [], 60 | "state_num": 2, 61 | "output_seq": 7, 62 | "ticker": {}, 63 | "recovery_journal": "matcher_in.0.1317086239.log" 64 | }, 65 | { 66 | "bids": [ 67 | { 68 | "price": 1024000000, 69 | "size": 1023400000, 70 | "order_id": 1, 71 | "user_id": 2 72 | }, 73 | { 74 | "price": 1023000000, 75 | "size": 1020000000, 76 | "order_id": 2, 77 | "user_id": 2 78 | } 79 | ], 80 | "asks": [], 81 | "state_num": 2, 82 | "output_seq": 8, 83 | "ticker": {}, 84 | "recovery_journal": "matcher_in.0.1317086239.log" 85 | }, 86 | { 87 | "bids": [ 88 | { 89 | "price": 1024000000, 90 | "size": 1023400000, 91 | "order_id": 1, 92 | "user_id": 2 93 | }, 94 | { 95 | "price": 1023000000, 96 | "size": 1020000000, 97 | "order_id": 2, 98 | "user_id": 2 99 | } 100 | ], 101 | "asks": [ 102 | { 103 | "price": 1800000000, 104 | "size": 8056912305, 105 | "order_id": 5, 106 | "user_id": 2 107 | } 108 | ], 109 | "state_num": 2, 110 | "output_seq": 10, 111 | "ticker": {}, 112 | "recovery_journal": "matcher_in.0.1317086239.log" 113 | }, 114 | { 115 | "bids": [ 116 | { 117 | "price": 1024000000, 118 | "size": 1023400000, 119 | "order_id": 1, 120 | "user_id": 2 121 | }, 122 | { 123 | "price": 1023000000, 124 | "size": 1020000000, 125 | "order_id": 2, 126 | "user_id": 2 127 | } 128 | ], 129 | "asks": [ 130 | { 131 | "price": 1100000000, 132 | "size": 120000000, 133 | "order_id": 3, 134 | "user_id": 2 135 | }, 136 | { 137 | "price": 1800000000, 138 | "size": 8056912305, 139 | "order_id": 5, 140 | "user_id": 2 141 | } 142 | ], 143 | "state_num": 2, 144 | "output_seq": 12, 145 | "ticker": {}, 146 | "recovery_journal": "matcher_in.0.1317086239.log" 147 | }, 148 | { 149 | "bids": [ 150 | { 151 | "price": 1024000000, 152 | "size": 1023400000, 153 | "order_id": 1, 154 | "user_id": 2 155 | }, 156 | { 157 | "price": 1023000000, 158 | "size": 1020000000, 159 | "order_id": 2, 160 | "user_id": 2 161 | } 162 | ], 163 | "asks": [ 164 | { 165 | "price": 1100000000, 166 | "size": 120000000, 167 | "order_id": 3, 168 | "user_id": 2 169 | }, 170 | { 171 | "price": 1100000000, 172 | "size": 210000000, 173 | "order_id": 4, 174 | "user_id": 2 175 | }, 176 | { 177 | "price": 1800000000, 178 | "size": 8056912305, 179 | "order_id": 5, 180 | "user_id": 2 181 | } 182 | ], 183 | "state_num": 2, 184 | "output_seq": 14, 185 | "ticker": {}, 186 | "recovery_journal": "matcher_in.0.1317086239.log" 187 | }, 188 | { 189 | "bids": [ 190 | { 191 | "price": 1024000000, 192 | "size": 1023400000, 193 | "order_id": 1, 194 | "user_id": 2 195 | } 196 | ], 197 | "asks": [ 198 | { 199 | "price": 1100000000, 200 | "size": 120000000, 201 | "order_id": 3, 202 | "user_id": 2 203 | }, 204 | { 205 | "price": 1100000000, 206 | "size": 210000000, 207 | "order_id": 4, 208 | "user_id": 2 209 | }, 210 | { 211 | "price": 1800000000, 212 | "size": 8056912305, 213 | "order_id": 5, 214 | "user_id": 2 215 | } 216 | ], 217 | "state_num": 2, 218 | "output_seq": 15, 219 | "ticker": {}, 220 | "recovery_journal": "matcher_in.0.1317086239.log" 221 | }, 222 | { 223 | "bids": [ 224 | { 225 | "price": 1024000000, 226 | "size": 1023400000, 227 | "order_id": 1, 228 | "user_id": 2 229 | } 230 | ], 231 | "asks": [ 232 | { 233 | "price": 1100000000, 234 | "size": 120000000, 235 | "order_id": 3, 236 | "user_id": 2 237 | }, 238 | { 239 | "price": 1100000000, 240 | "size": 210000000, 241 | "order_id": 4, 242 | "user_id": 2 243 | }, 244 | { 245 | "price": 1800000000, 246 | "size": 8056912305, 247 | "order_id": 5, 248 | "user_id": 2 249 | } 250 | ], 251 | "state_num": 2, 252 | "output_seq": 15, 253 | "ticker": {}, 254 | "recovery_journal": "matcher_in.0.1317086239.log" 255 | } 256 | ] -------------------------------------------------------------------------------- /test/unit/add_fill_cancel/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1023000000,"user_id":2,"side":0,"size":5234591011},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1023000000,"user_id":3,"side":1,"size":1023400000},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":1,"user_id":2},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":0,"user_id":2},"seq":4} 6 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":3,"price":220000000,"user_id":3,"side":0,"size":1010000000},"seq":5} 7 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":4,"price":220000000,"user_id":2,"side":1,"size":1010000000},"seq":6} 8 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":4,"user_id":2},"seq":7} 9 | {"timestamp":1317086239.693123,"type":"cancel","payload":{"order_id":3,"user_id":3},"seq":8} 10 | {"type":"state","payload":1} 11 | -------------------------------------------------------------------------------- /test/unit/add_fill_cancel/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "cancel_reject", 4 | "timestamp": 1317086239.693123, 5 | "target_id": 2, 6 | "product_id": 0, 7 | "payload": { 8 | "order_id": 1, 9 | "reject_reason": "not found" 10 | }, 11 | "seq": 1 12 | }, 13 | { 14 | "type": "cancel_reject", 15 | "timestamp": 1317086239.693123, 16 | "target_id": 2, 17 | "product_id": 0, 18 | "payload": { 19 | "order_id": 4, 20 | "reject_reason": "not found" 21 | }, 22 | "seq": 2 23 | }, 24 | { 25 | "type": "cancel_reject", 26 | "timestamp": 1317086239.693123, 27 | "target_id": 3, 28 | "product_id": 0, 29 | "payload": { 30 | "order_id": 3, 31 | "reject_reason": "not found" 32 | }, 33 | "seq": 3 34 | }, 35 | { 36 | "type": "state", 37 | "payload": { 38 | "bids": [], 39 | "asks": [], 40 | "state_num": 2, 41 | "output_seq": 13, 42 | "ticker": { 43 | "price": 220000000, 44 | "size": 1010000000, 45 | "timestamp": 1317086239.693123 46 | }, 47 | "recovery_journal": "matcher_in.0.1317086239.log" 48 | }, 49 | "timestamp": 1317086239.693123, 50 | "seq": 4 51 | } 52 | ] -------------------------------------------------------------------------------- /test/unit/add_fill_cancel/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": 2, 12 | "price": 1023000000, 13 | "size": 5234591011, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": 2, 27 | "price": 1023000000, 28 | "size": 5234591011, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 1, 40 | "order_id": 1, 41 | "user_id": 3, 42 | "price": 1023000000, 43 | "size": 1023400000, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "match", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "taker_id": 1, 54 | "provider_id": 0, 55 | "taker_user_id": 3, 56 | "provider_user_id": 2, 57 | "size": 1023400000, 58 | "price": 1023000000, 59 | "provider_side": 0, 60 | "timestamp": 1317086239.693123 61 | } 62 | }, 63 | { 64 | "type": "order_status", 65 | "timestamp": 1317086239.693123, 66 | "product_id": 0, 67 | "seq": 5, 68 | "payload": { 69 | "order_id": 1, 70 | "status": "done", 71 | "size": 0, 72 | "user_id": 3, 73 | "reason": "filled", 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "order_id": 0, 84 | "status": "done", 85 | "size": 4211191011, 86 | "user_id": 2, 87 | "reason": "cancelled", 88 | "timestamp": 1317086239.693123 89 | } 90 | }, 91 | { 92 | "type": "order_status", 93 | "timestamp": 1317086239.693123, 94 | "product_id": 0, 95 | "seq": 7, 96 | "payload": { 97 | "status": "received", 98 | "side": 0, 99 | "order_id": 3, 100 | "user_id": 3, 101 | "price": 220000000, 102 | "size": 1010000000, 103 | "timestamp": 1317086239.693123 104 | } 105 | }, 106 | { 107 | "type": "order_status", 108 | "timestamp": 1317086239.693123, 109 | "product_id": 0, 110 | "seq": 8, 111 | "payload": { 112 | "status": "open", 113 | "side": 0, 114 | "order_id": 3, 115 | "user_id": 3, 116 | "price": 220000000, 117 | "size": 1010000000, 118 | "timestamp": 1317086239.693123 119 | } 120 | }, 121 | { 122 | "type": "order_status", 123 | "timestamp": 1317086239.693123, 124 | "product_id": 0, 125 | "seq": 9, 126 | "payload": { 127 | "status": "received", 128 | "side": 1, 129 | "order_id": 4, 130 | "user_id": 2, 131 | "price": 220000000, 132 | "size": 1010000000, 133 | "timestamp": 1317086239.693123 134 | } 135 | }, 136 | { 137 | "type": "match", 138 | "timestamp": 1317086239.693123, 139 | "product_id": 0, 140 | "seq": 10, 141 | "payload": { 142 | "taker_id": 4, 143 | "provider_id": 3, 144 | "taker_user_id": 2, 145 | "provider_user_id": 3, 146 | "size": 1010000000, 147 | "price": 220000000, 148 | "provider_side": 0, 149 | "timestamp": 1317086239.693123 150 | } 151 | }, 152 | { 153 | "type": "order_status", 154 | "timestamp": 1317086239.693123, 155 | "product_id": 0, 156 | "seq": 11, 157 | "payload": { 158 | "order_id": 3, 159 | "status": "done", 160 | "size": 0, 161 | "user_id": 3, 162 | "reason": "filled", 163 | "timestamp": 1317086239.693123 164 | } 165 | }, 166 | { 167 | "type": "order_status", 168 | "timestamp": 1317086239.693123, 169 | "product_id": 0, 170 | "seq": 12, 171 | "payload": { 172 | "order_id": 4, 173 | "status": "done", 174 | "size": 0, 175 | "user_id": 2, 176 | "reason": "filled", 177 | "timestamp": 1317086239.693123 178 | } 179 | } 180 | ] -------------------------------------------------------------------------------- /test/unit/add_fill_cancel/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794502187, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": 2, 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794502187, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1023000000, 19 | "user_id": 3, 20 | "side": 1, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794502187, 26 | "type": "cancel", 27 | "payload": { 28 | "order_id": 1, 29 | "user_id": 2 30 | } 31 | }, 32 | { 33 | "timestamp": 1311794502187, 34 | "type": "cancel", 35 | "payload": { 36 | "order_id": 0, 37 | "user_id": 2 38 | } 39 | }, 40 | { 41 | "timestamp": 1311794502188, 42 | "type": "order", 43 | "payload": { 44 | "order_id": 3, 45 | "price": 220000000, 46 | "user_id": 3, 47 | "side": 0, 48 | "size": 1010000000 49 | } 50 | }, 51 | { 52 | "timestamp": 1311794502188, 53 | "type": "order", 54 | "payload": { 55 | "order_id": 4, 56 | "price": 220000000, 57 | "user_id": 2, 58 | "side": 1, 59 | "size": 1010000000 60 | } 61 | }, 62 | { 63 | "timestamp": 1311794502189, 64 | "type": "cancel", 65 | "payload": { 66 | "order_id": 4, 67 | "user_id": 2 68 | } 69 | }, 70 | { 71 | "timestamp": 1311794502189, 72 | "type": "cancel", 73 | "payload": { 74 | "order_id": 3, 75 | "user_id": 3 76 | } 77 | } 78 | ] 79 | -------------------------------------------------------------------------------- /test/unit/add_fill_cancel/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1023000000, 6 | "size": 5234591011, 7 | "order_id": 0, 8 | "user_id": 2 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1023000000, 21 | "size": 4211191011, 22 | "order_id": 0, 23 | "user_id": 2 24 | } 25 | ], 26 | "asks": [], 27 | "state_num": 2, 28 | "output_seq": 6, 29 | "ticker": { 30 | "price": 1023000000, 31 | "size": 1023400000, 32 | "timestamp": 1317086239.693123 33 | }, 34 | "recovery_journal": "matcher_in.0.1317086239.log" 35 | }, 36 | { 37 | "bids": [ 38 | { 39 | "price": 1023000000, 40 | "size": 4211191011, 41 | "order_id": 0, 42 | "user_id": 2 43 | } 44 | ], 45 | "asks": [], 46 | "state_num": 2, 47 | "output_seq": 6, 48 | "ticker": { 49 | "price": 1023000000, 50 | "size": 1023400000, 51 | "timestamp": 1317086239.693123 52 | }, 53 | "recovery_journal": "matcher_in.0.1317086239.log" 54 | }, 55 | { 56 | "bids": [], 57 | "asks": [], 58 | "state_num": 2, 59 | "output_seq": 7, 60 | "ticker": { 61 | "price": 1023000000, 62 | "size": 1023400000, 63 | "timestamp": 1317086239.693123 64 | }, 65 | "recovery_journal": "matcher_in.0.1317086239.log" 66 | }, 67 | { 68 | "bids": [ 69 | { 70 | "price": 220000000, 71 | "size": 1010000000, 72 | "order_id": 3, 73 | "user_id": 3 74 | } 75 | ], 76 | "asks": [], 77 | "state_num": 2, 78 | "output_seq": 9, 79 | "ticker": { 80 | "price": 1023000000, 81 | "size": 1023400000, 82 | "timestamp": 1317086239.693123 83 | }, 84 | "recovery_journal": "matcher_in.0.1317086239.log" 85 | }, 86 | { 87 | "bids": [], 88 | "asks": [], 89 | "state_num": 2, 90 | "output_seq": 13, 91 | "ticker": { 92 | "price": 220000000, 93 | "size": 1010000000, 94 | "timestamp": 1317086239.693123 95 | }, 96 | "recovery_journal": "matcher_in.0.1317086239.log" 97 | }, 98 | { 99 | "bids": [], 100 | "asks": [], 101 | "state_num": 2, 102 | "output_seq": 13, 103 | "ticker": { 104 | "price": 220000000, 105 | "size": 1010000000, 106 | "timestamp": 1317086239.693123 107 | }, 108 | "recovery_journal": "matcher_in.0.1317086239.log" 109 | }, 110 | { 111 | "bids": [], 112 | "asks": [], 113 | "state_num": 2, 114 | "output_seq": 13, 115 | "ticker": { 116 | "price": 220000000, 117 | "size": 1010000000, 118 | "timestamp": 1317086239.693123 119 | }, 120 | "recovery_journal": "matcher_in.0.1317086239.log" 121 | } 122 | ] -------------------------------------------------------------------------------- /test/unit/match_full/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1023000000,"user_id":2,"side":0,"size":5234591011},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1024000000,"user_id":2,"side":0,"size":1023400000},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":2,"price":1023000000,"user_id":2,"side":0,"size":1020000000},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":5,"price":1800000000,"user_id":3,"side":1,"size":8056912305},"seq":4} 6 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":3,"price":1100000000,"user_id":3,"side":1,"size":120000000},"seq":5} 7 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":4,"price":1100000000,"user_id":3,"side":1,"size":210000000},"seq":6} 8 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":7,"price":1100000000,"user_id":2,"side":0,"size":120000000},"seq":7} 9 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":9,"price":1024000000,"user_id":3,"side":1,"size":1023400000},"seq":8} 10 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":10,"price":1100000000,"user_id":2,"side":0,"size":210000000},"seq":9} 11 | {"type":"state","payload":1} 12 | -------------------------------------------------------------------------------- /test/unit/match_full/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state", 4 | "payload": { 5 | "bids": [ 6 | { 7 | "price": 1023000000, 8 | "size": 5234591011, 9 | "order_id": 0, 10 | "user_id": 2 11 | }, 12 | { 13 | "price": 1023000000, 14 | "size": 1020000000, 15 | "order_id": 2, 16 | "user_id": 2 17 | } 18 | ], 19 | "asks": [ 20 | { 21 | "price": 1800000000, 22 | "size": 8056912305, 23 | "order_id": 5, 24 | "user_id": 3 25 | } 26 | ], 27 | "state_num": 2, 28 | "output_seq": 25, 29 | "ticker": { 30 | "price": 1100000000, 31 | "size": 210000000, 32 | "timestamp": 1317086239.693123 33 | }, 34 | "recovery_journal": "matcher_in.0.1317086239.log" 35 | }, 36 | "timestamp": 1317086239.693123, 37 | "seq": 1 38 | } 39 | ] -------------------------------------------------------------------------------- /test/unit/match_full/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": 2, 12 | "price": 1023000000, 13 | "size": 5234591011, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": 2, 27 | "price": 1023000000, 28 | "size": 5234591011, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 0, 40 | "order_id": 1, 41 | "user_id": 2, 42 | "price": 1024000000, 43 | "size": 1023400000, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "order_status", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "status": "open", 54 | "side": 0, 55 | "order_id": 1, 56 | "user_id": 2, 57 | "price": 1024000000, 58 | "size": 1023400000, 59 | "timestamp": 1317086239.693123 60 | } 61 | }, 62 | { 63 | "type": "order_status", 64 | "timestamp": 1317086239.693123, 65 | "product_id": 0, 66 | "seq": 5, 67 | "payload": { 68 | "status": "received", 69 | "side": 0, 70 | "order_id": 2, 71 | "user_id": 2, 72 | "price": 1023000000, 73 | "size": 1020000000, 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "status": "open", 84 | "side": 0, 85 | "order_id": 2, 86 | "user_id": 2, 87 | "price": 1023000000, 88 | "size": 1020000000, 89 | "timestamp": 1317086239.693123 90 | } 91 | }, 92 | { 93 | "type": "order_status", 94 | "timestamp": 1317086239.693123, 95 | "product_id": 0, 96 | "seq": 7, 97 | "payload": { 98 | "status": "received", 99 | "side": 1, 100 | "order_id": 5, 101 | "user_id": 3, 102 | "price": 1800000000, 103 | "size": 8056912305, 104 | "timestamp": 1317086239.693123 105 | } 106 | }, 107 | { 108 | "type": "order_status", 109 | "timestamp": 1317086239.693123, 110 | "product_id": 0, 111 | "seq": 8, 112 | "payload": { 113 | "status": "open", 114 | "side": 1, 115 | "order_id": 5, 116 | "user_id": 3, 117 | "price": 1800000000, 118 | "size": 8056912305, 119 | "timestamp": 1317086239.693123 120 | } 121 | }, 122 | { 123 | "type": "order_status", 124 | "timestamp": 1317086239.693123, 125 | "product_id": 0, 126 | "seq": 9, 127 | "payload": { 128 | "status": "received", 129 | "side": 1, 130 | "order_id": 3, 131 | "user_id": 3, 132 | "price": 1100000000, 133 | "size": 120000000, 134 | "timestamp": 1317086239.693123 135 | } 136 | }, 137 | { 138 | "type": "order_status", 139 | "timestamp": 1317086239.693123, 140 | "product_id": 0, 141 | "seq": 10, 142 | "payload": { 143 | "status": "open", 144 | "side": 1, 145 | "order_id": 3, 146 | "user_id": 3, 147 | "price": 1100000000, 148 | "size": 120000000, 149 | "timestamp": 1317086239.693123 150 | } 151 | }, 152 | { 153 | "type": "order_status", 154 | "timestamp": 1317086239.693123, 155 | "product_id": 0, 156 | "seq": 11, 157 | "payload": { 158 | "status": "received", 159 | "side": 1, 160 | "order_id": 4, 161 | "user_id": 3, 162 | "price": 1100000000, 163 | "size": 210000000, 164 | "timestamp": 1317086239.693123 165 | } 166 | }, 167 | { 168 | "type": "order_status", 169 | "timestamp": 1317086239.693123, 170 | "product_id": 0, 171 | "seq": 12, 172 | "payload": { 173 | "status": "open", 174 | "side": 1, 175 | "order_id": 4, 176 | "user_id": 3, 177 | "price": 1100000000, 178 | "size": 210000000, 179 | "timestamp": 1317086239.693123 180 | } 181 | }, 182 | { 183 | "type": "order_status", 184 | "timestamp": 1317086239.693123, 185 | "product_id": 0, 186 | "seq": 13, 187 | "payload": { 188 | "status": "received", 189 | "side": 0, 190 | "order_id": 7, 191 | "user_id": 2, 192 | "price": 1100000000, 193 | "size": 120000000, 194 | "timestamp": 1317086239.693123 195 | } 196 | }, 197 | { 198 | "type": "match", 199 | "timestamp": 1317086239.693123, 200 | "product_id": 0, 201 | "seq": 14, 202 | "payload": { 203 | "taker_id": 7, 204 | "provider_id": 3, 205 | "taker_user_id": 2, 206 | "provider_user_id": 3, 207 | "size": 120000000, 208 | "price": 1100000000, 209 | "provider_side": 1, 210 | "timestamp": 1317086239.693123 211 | } 212 | }, 213 | { 214 | "type": "order_status", 215 | "timestamp": 1317086239.693123, 216 | "product_id": 0, 217 | "seq": 15, 218 | "payload": { 219 | "order_id": 3, 220 | "status": "done", 221 | "size": 0, 222 | "user_id": 3, 223 | "reason": "filled", 224 | "timestamp": 1317086239.693123 225 | } 226 | }, 227 | { 228 | "type": "order_status", 229 | "timestamp": 1317086239.693123, 230 | "product_id": 0, 231 | "seq": 16, 232 | "payload": { 233 | "order_id": 7, 234 | "status": "done", 235 | "size": 0, 236 | "user_id": 2, 237 | "reason": "filled", 238 | "timestamp": 1317086239.693123 239 | } 240 | }, 241 | { 242 | "type": "order_status", 243 | "timestamp": 1317086239.693123, 244 | "product_id": 0, 245 | "seq": 17, 246 | "payload": { 247 | "status": "received", 248 | "side": 1, 249 | "order_id": 9, 250 | "user_id": 3, 251 | "price": 1024000000, 252 | "size": 1023400000, 253 | "timestamp": 1317086239.693123 254 | } 255 | }, 256 | { 257 | "type": "match", 258 | "timestamp": 1317086239.693123, 259 | "product_id": 0, 260 | "seq": 18, 261 | "payload": { 262 | "taker_id": 9, 263 | "provider_id": 1, 264 | "taker_user_id": 3, 265 | "provider_user_id": 2, 266 | "size": 1023400000, 267 | "price": 1024000000, 268 | "provider_side": 0, 269 | "timestamp": 1317086239.693123 270 | } 271 | }, 272 | { 273 | "type": "order_status", 274 | "timestamp": 1317086239.693123, 275 | "product_id": 0, 276 | "seq": 19, 277 | "payload": { 278 | "order_id": 1, 279 | "status": "done", 280 | "size": 0, 281 | "user_id": 2, 282 | "reason": "filled", 283 | "timestamp": 1317086239.693123 284 | } 285 | }, 286 | { 287 | "type": "order_status", 288 | "timestamp": 1317086239.693123, 289 | "product_id": 0, 290 | "seq": 20, 291 | "payload": { 292 | "order_id": 9, 293 | "status": "done", 294 | "size": 0, 295 | "user_id": 3, 296 | "reason": "filled", 297 | "timestamp": 1317086239.693123 298 | } 299 | }, 300 | { 301 | "type": "order_status", 302 | "timestamp": 1317086239.693123, 303 | "product_id": 0, 304 | "seq": 21, 305 | "payload": { 306 | "status": "received", 307 | "side": 0, 308 | "order_id": 10, 309 | "user_id": 2, 310 | "price": 1100000000, 311 | "size": 210000000, 312 | "timestamp": 1317086239.693123 313 | } 314 | }, 315 | { 316 | "type": "match", 317 | "timestamp": 1317086239.693123, 318 | "product_id": 0, 319 | "seq": 22, 320 | "payload": { 321 | "taker_id": 10, 322 | "provider_id": 4, 323 | "taker_user_id": 2, 324 | "provider_user_id": 3, 325 | "size": 210000000, 326 | "price": 1100000000, 327 | "provider_side": 1, 328 | "timestamp": 1317086239.693123 329 | } 330 | }, 331 | { 332 | "type": "order_status", 333 | "timestamp": 1317086239.693123, 334 | "product_id": 0, 335 | "seq": 23, 336 | "payload": { 337 | "order_id": 4, 338 | "status": "done", 339 | "size": 0, 340 | "user_id": 3, 341 | "reason": "filled", 342 | "timestamp": 1317086239.693123 343 | } 344 | }, 345 | { 346 | "type": "order_status", 347 | "timestamp": 1317086239.693123, 348 | "product_id": 0, 349 | "seq": 24, 350 | "payload": { 351 | "order_id": 10, 352 | "status": "done", 353 | "size": 0, 354 | "user_id": 2, 355 | "reason": "filled", 356 | "timestamp": 1317086239.693123 357 | } 358 | } 359 | ] -------------------------------------------------------------------------------- /test/unit/match_full/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794516561, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": 2, 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794516561, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": 2, 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794516561, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": 2, 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794516562, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1800000000, 41 | "user_id": 3, 42 | "side": 1, 43 | "size": 8056912305 44 | } 45 | }, 46 | { 47 | "timestamp": 1311794516562, 48 | "type": "order", 49 | "payload": { 50 | "order_id": 3, 51 | "price": 1100000000, 52 | "user_id": 3, 53 | "side": 1, 54 | "size": 120000000 55 | } 56 | }, 57 | { 58 | "timestamp": 1311794516562, 59 | "type": "order", 60 | "payload": { 61 | "order_id": 4, 62 | "price": 1100000000, 63 | "user_id": 3, 64 | "side": 1, 65 | "size": 210000000 66 | } 67 | }, 68 | { 69 | "timestamp": 1311794516562, 70 | "type": "order", 71 | "payload": { 72 | "order_id": 7, 73 | "price": 1100000000, 74 | "user_id": 2, 75 | "side": 0, 76 | "size": 120000000 77 | } 78 | }, 79 | { 80 | "timestamp": 1311794516562, 81 | "type": "order", 82 | "payload": { 83 | "order_id": 9, 84 | "price": 1024000000, 85 | "user_id": 3, 86 | "side": 1, 87 | "size": 1023400000 88 | } 89 | }, 90 | { 91 | "timestamp": 1311794516562, 92 | "type": "order", 93 | "payload": { 94 | "order_id": 10, 95 | "price": 1100000000, 96 | "user_id": 2, 97 | "side": 0, 98 | "size": 210000000 99 | } 100 | } 101 | ] 102 | -------------------------------------------------------------------------------- /test/unit/match_full/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1023000000, 6 | "size": 5234591011, 7 | "order_id": 0, 8 | "user_id": 2 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1024000000, 21 | "size": 1023400000, 22 | "order_id": 1, 23 | "user_id": 2 24 | }, 25 | { 26 | "price": 1023000000, 27 | "size": 5234591011, 28 | "order_id": 0, 29 | "user_id": 2 30 | } 31 | ], 32 | "asks": [], 33 | "state_num": 2, 34 | "output_seq": 5, 35 | "ticker": {}, 36 | "recovery_journal": "matcher_in.0.1317086239.log" 37 | }, 38 | { 39 | "bids": [ 40 | { 41 | "price": 1024000000, 42 | "size": 1023400000, 43 | "order_id": 1, 44 | "user_id": 2 45 | }, 46 | { 47 | "price": 1023000000, 48 | "size": 5234591011, 49 | "order_id": 0, 50 | "user_id": 2 51 | }, 52 | { 53 | "price": 1023000000, 54 | "size": 1020000000, 55 | "order_id": 2, 56 | "user_id": 2 57 | } 58 | ], 59 | "asks": [], 60 | "state_num": 2, 61 | "output_seq": 7, 62 | "ticker": {}, 63 | "recovery_journal": "matcher_in.0.1317086239.log" 64 | }, 65 | { 66 | "bids": [ 67 | { 68 | "price": 1024000000, 69 | "size": 1023400000, 70 | "order_id": 1, 71 | "user_id": 2 72 | }, 73 | { 74 | "price": 1023000000, 75 | "size": 5234591011, 76 | "order_id": 0, 77 | "user_id": 2 78 | }, 79 | { 80 | "price": 1023000000, 81 | "size": 1020000000, 82 | "order_id": 2, 83 | "user_id": 2 84 | } 85 | ], 86 | "asks": [ 87 | { 88 | "price": 1800000000, 89 | "size": 8056912305, 90 | "order_id": 5, 91 | "user_id": 3 92 | } 93 | ], 94 | "state_num": 2, 95 | "output_seq": 9, 96 | "ticker": {}, 97 | "recovery_journal": "matcher_in.0.1317086239.log" 98 | }, 99 | { 100 | "bids": [ 101 | { 102 | "price": 1024000000, 103 | "size": 1023400000, 104 | "order_id": 1, 105 | "user_id": 2 106 | }, 107 | { 108 | "price": 1023000000, 109 | "size": 5234591011, 110 | "order_id": 0, 111 | "user_id": 2 112 | }, 113 | { 114 | "price": 1023000000, 115 | "size": 1020000000, 116 | "order_id": 2, 117 | "user_id": 2 118 | } 119 | ], 120 | "asks": [ 121 | { 122 | "price": 1100000000, 123 | "size": 120000000, 124 | "order_id": 3, 125 | "user_id": 3 126 | }, 127 | { 128 | "price": 1800000000, 129 | "size": 8056912305, 130 | "order_id": 5, 131 | "user_id": 3 132 | } 133 | ], 134 | "state_num": 2, 135 | "output_seq": 11, 136 | "ticker": {}, 137 | "recovery_journal": "matcher_in.0.1317086239.log" 138 | }, 139 | { 140 | "bids": [ 141 | { 142 | "price": 1024000000, 143 | "size": 1023400000, 144 | "order_id": 1, 145 | "user_id": 2 146 | }, 147 | { 148 | "price": 1023000000, 149 | "size": 5234591011, 150 | "order_id": 0, 151 | "user_id": 2 152 | }, 153 | { 154 | "price": 1023000000, 155 | "size": 1020000000, 156 | "order_id": 2, 157 | "user_id": 2 158 | } 159 | ], 160 | "asks": [ 161 | { 162 | "price": 1100000000, 163 | "size": 120000000, 164 | "order_id": 3, 165 | "user_id": 3 166 | }, 167 | { 168 | "price": 1100000000, 169 | "size": 210000000, 170 | "order_id": 4, 171 | "user_id": 3 172 | }, 173 | { 174 | "price": 1800000000, 175 | "size": 8056912305, 176 | "order_id": 5, 177 | "user_id": 3 178 | } 179 | ], 180 | "state_num": 2, 181 | "output_seq": 13, 182 | "ticker": {}, 183 | "recovery_journal": "matcher_in.0.1317086239.log" 184 | }, 185 | { 186 | "bids": [ 187 | { 188 | "price": 1024000000, 189 | "size": 1023400000, 190 | "order_id": 1, 191 | "user_id": 2 192 | }, 193 | { 194 | "price": 1023000000, 195 | "size": 5234591011, 196 | "order_id": 0, 197 | "user_id": 2 198 | }, 199 | { 200 | "price": 1023000000, 201 | "size": 1020000000, 202 | "order_id": 2, 203 | "user_id": 2 204 | } 205 | ], 206 | "asks": [ 207 | { 208 | "price": 1100000000, 209 | "size": 210000000, 210 | "order_id": 4, 211 | "user_id": 3 212 | }, 213 | { 214 | "price": 1800000000, 215 | "size": 8056912305, 216 | "order_id": 5, 217 | "user_id": 3 218 | } 219 | ], 220 | "state_num": 2, 221 | "output_seq": 17, 222 | "ticker": { 223 | "price": 1100000000, 224 | "size": 120000000, 225 | "timestamp": 1317086239.693123 226 | }, 227 | "recovery_journal": "matcher_in.0.1317086239.log" 228 | }, 229 | { 230 | "bids": [ 231 | { 232 | "price": 1023000000, 233 | "size": 5234591011, 234 | "order_id": 0, 235 | "user_id": 2 236 | }, 237 | { 238 | "price": 1023000000, 239 | "size": 1020000000, 240 | "order_id": 2, 241 | "user_id": 2 242 | } 243 | ], 244 | "asks": [ 245 | { 246 | "price": 1100000000, 247 | "size": 210000000, 248 | "order_id": 4, 249 | "user_id": 3 250 | }, 251 | { 252 | "price": 1800000000, 253 | "size": 8056912305, 254 | "order_id": 5, 255 | "user_id": 3 256 | } 257 | ], 258 | "state_num": 2, 259 | "output_seq": 21, 260 | "ticker": { 261 | "price": 1024000000, 262 | "size": 1023400000, 263 | "timestamp": 1317086239.693123 264 | }, 265 | "recovery_journal": "matcher_in.0.1317086239.log" 266 | }, 267 | { 268 | "bids": [ 269 | { 270 | "price": 1023000000, 271 | "size": 5234591011, 272 | "order_id": 0, 273 | "user_id": 2 274 | }, 275 | { 276 | "price": 1023000000, 277 | "size": 1020000000, 278 | "order_id": 2, 279 | "user_id": 2 280 | } 281 | ], 282 | "asks": [ 283 | { 284 | "price": 1800000000, 285 | "size": 8056912305, 286 | "order_id": 5, 287 | "user_id": 3 288 | } 289 | ], 290 | "state_num": 2, 291 | "output_seq": 25, 292 | "ticker": { 293 | "price": 1100000000, 294 | "size": 210000000, 295 | "timestamp": 1317086239.693123 296 | }, 297 | "recovery_journal": "matcher_in.0.1317086239.log" 298 | } 299 | ] -------------------------------------------------------------------------------- /test/unit/match_over/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1023000000,"user_id":"2","side":0,"size":5234591011},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1024000000,"user_id":"3","side":0,"size":1023400000},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":2,"price":1023000000,"user_id":"4","side":0,"size":1020000000},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":5,"price":1800000000,"user_id":"5","side":1,"size":8056912305},"seq":4} 6 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":3,"price":1100000000,"user_id":"6","side":1,"size":120000000},"seq":5} 7 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":4,"price":1100000000,"user_id":"7","side":1,"size":210000000},"seq":6} 8 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":9,"price":1000000,"user_id":"8","side":1,"size":10010000000},"seq":7} 9 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":9,"price":10000000000,"user_id":"9","side":0,"size":20030000000},"seq":8} 10 | {"type":"state","payload":1} 11 | -------------------------------------------------------------------------------- /test/unit/match_over/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state", 4 | "payload": { 5 | "bids": [ 6 | { 7 | "price": 10000000000, 8 | "size": 8911078706, 9 | "order_id": 9, 10 | "user_id": "9" 11 | } 12 | ], 13 | "asks": [], 14 | "state_num": 2, 15 | "output_seq": 31, 16 | "ticker": { 17 | "price": 1800000000, 18 | "size": 8056912305, 19 | "timestamp": 1317086239.693123 20 | }, 21 | "recovery_journal": "matcher_in.0.1317086239.log" 22 | }, 23 | "timestamp": 1317086239.693123, 24 | "seq": 1 25 | } 26 | ] -------------------------------------------------------------------------------- /test/unit/match_over/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": "2", 12 | "price": 1023000000, 13 | "size": 5234591011, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": "2", 27 | "price": 1023000000, 28 | "size": 5234591011, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 0, 40 | "order_id": 1, 41 | "user_id": "3", 42 | "price": 1024000000, 43 | "size": 1023400000, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "order_status", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "status": "open", 54 | "side": 0, 55 | "order_id": 1, 56 | "user_id": "3", 57 | "price": 1024000000, 58 | "size": 1023400000, 59 | "timestamp": 1317086239.693123 60 | } 61 | }, 62 | { 63 | "type": "order_status", 64 | "timestamp": 1317086239.693123, 65 | "product_id": 0, 66 | "seq": 5, 67 | "payload": { 68 | "status": "received", 69 | "side": 0, 70 | "order_id": 2, 71 | "user_id": "4", 72 | "price": 1023000000, 73 | "size": 1020000000, 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "status": "open", 84 | "side": 0, 85 | "order_id": 2, 86 | "user_id": "4", 87 | "price": 1023000000, 88 | "size": 1020000000, 89 | "timestamp": 1317086239.693123 90 | } 91 | }, 92 | { 93 | "type": "order_status", 94 | "timestamp": 1317086239.693123, 95 | "product_id": 0, 96 | "seq": 7, 97 | "payload": { 98 | "status": "received", 99 | "side": 1, 100 | "order_id": 5, 101 | "user_id": "5", 102 | "price": 1800000000, 103 | "size": 8056912305, 104 | "timestamp": 1317086239.693123 105 | } 106 | }, 107 | { 108 | "type": "order_status", 109 | "timestamp": 1317086239.693123, 110 | "product_id": 0, 111 | "seq": 8, 112 | "payload": { 113 | "status": "open", 114 | "side": 1, 115 | "order_id": 5, 116 | "user_id": "5", 117 | "price": 1800000000, 118 | "size": 8056912305, 119 | "timestamp": 1317086239.693123 120 | } 121 | }, 122 | { 123 | "type": "order_status", 124 | "timestamp": 1317086239.693123, 125 | "product_id": 0, 126 | "seq": 9, 127 | "payload": { 128 | "status": "received", 129 | "side": 1, 130 | "order_id": 3, 131 | "user_id": "6", 132 | "price": 1100000000, 133 | "size": 120000000, 134 | "timestamp": 1317086239.693123 135 | } 136 | }, 137 | { 138 | "type": "order_status", 139 | "timestamp": 1317086239.693123, 140 | "product_id": 0, 141 | "seq": 10, 142 | "payload": { 143 | "status": "open", 144 | "side": 1, 145 | "order_id": 3, 146 | "user_id": "6", 147 | "price": 1100000000, 148 | "size": 120000000, 149 | "timestamp": 1317086239.693123 150 | } 151 | }, 152 | { 153 | "type": "order_status", 154 | "timestamp": 1317086239.693123, 155 | "product_id": 0, 156 | "seq": 11, 157 | "payload": { 158 | "status": "received", 159 | "side": 1, 160 | "order_id": 4, 161 | "user_id": "7", 162 | "price": 1100000000, 163 | "size": 210000000, 164 | "timestamp": 1317086239.693123 165 | } 166 | }, 167 | { 168 | "type": "order_status", 169 | "timestamp": 1317086239.693123, 170 | "product_id": 0, 171 | "seq": 12, 172 | "payload": { 173 | "status": "open", 174 | "side": 1, 175 | "order_id": 4, 176 | "user_id": "7", 177 | "price": 1100000000, 178 | "size": 210000000, 179 | "timestamp": 1317086239.693123 180 | } 181 | }, 182 | { 183 | "type": "order_status", 184 | "timestamp": 1317086239.693123, 185 | "product_id": 0, 186 | "seq": 13, 187 | "payload": { 188 | "status": "received", 189 | "side": 1, 190 | "order_id": 9, 191 | "user_id": "8", 192 | "price": 1000000, 193 | "size": 10010000000, 194 | "timestamp": 1317086239.693123 195 | } 196 | }, 197 | { 198 | "type": "match", 199 | "timestamp": 1317086239.693123, 200 | "product_id": 0, 201 | "seq": 14, 202 | "payload": { 203 | "taker_id": 9, 204 | "provider_id": 1, 205 | "taker_user_id": "8", 206 | "provider_user_id": "3", 207 | "size": 1023400000, 208 | "price": 1024000000, 209 | "provider_side": 0, 210 | "timestamp": 1317086239.693123 211 | } 212 | }, 213 | { 214 | "type": "order_status", 215 | "timestamp": 1317086239.693123, 216 | "product_id": 0, 217 | "seq": 15, 218 | "payload": { 219 | "order_id": 1, 220 | "status": "done", 221 | "size": 0, 222 | "user_id": "3", 223 | "reason": "filled", 224 | "timestamp": 1317086239.693123 225 | } 226 | }, 227 | { 228 | "type": "match", 229 | "timestamp": 1317086239.693123, 230 | "product_id": 0, 231 | "seq": 16, 232 | "payload": { 233 | "taker_id": 9, 234 | "provider_id": 0, 235 | "taker_user_id": "8", 236 | "provider_user_id": "2", 237 | "size": 5234591011, 238 | "price": 1023000000, 239 | "provider_side": 0, 240 | "timestamp": 1317086239.693123 241 | } 242 | }, 243 | { 244 | "type": "order_status", 245 | "timestamp": 1317086239.693123, 246 | "product_id": 0, 247 | "seq": 17, 248 | "payload": { 249 | "order_id": 0, 250 | "status": "done", 251 | "size": 0, 252 | "user_id": "2", 253 | "reason": "filled", 254 | "timestamp": 1317086239.693123 255 | } 256 | }, 257 | { 258 | "type": "match", 259 | "timestamp": 1317086239.693123, 260 | "product_id": 0, 261 | "seq": 18, 262 | "payload": { 263 | "taker_id": 9, 264 | "provider_id": 2, 265 | "taker_user_id": "8", 266 | "provider_user_id": "4", 267 | "size": 1020000000, 268 | "price": 1023000000, 269 | "provider_side": 0, 270 | "timestamp": 1317086239.693123 271 | } 272 | }, 273 | { 274 | "type": "order_status", 275 | "timestamp": 1317086239.693123, 276 | "product_id": 0, 277 | "seq": 19, 278 | "payload": { 279 | "order_id": 2, 280 | "status": "done", 281 | "size": 0, 282 | "user_id": "4", 283 | "reason": "filled", 284 | "timestamp": 1317086239.693123 285 | } 286 | }, 287 | { 288 | "type": "order_status", 289 | "timestamp": 1317086239.693123, 290 | "product_id": 0, 291 | "seq": 20, 292 | "payload": { 293 | "status": "open", 294 | "side": 1, 295 | "order_id": 9, 296 | "user_id": "8", 297 | "price": 1000000, 298 | "size": 2732008989, 299 | "timestamp": 1317086239.693123 300 | } 301 | }, 302 | { 303 | "type": "order_status", 304 | "timestamp": 1317086239.693123, 305 | "product_id": 0, 306 | "seq": 21, 307 | "payload": { 308 | "status": "received", 309 | "side": 0, 310 | "order_id": 9, 311 | "user_id": "9", 312 | "price": 10000000000, 313 | "size": 20030000000, 314 | "timestamp": 1317086239.693123 315 | } 316 | }, 317 | { 318 | "type": "match", 319 | "timestamp": 1317086239.693123, 320 | "product_id": 0, 321 | "seq": 22, 322 | "payload": { 323 | "taker_id": 9, 324 | "provider_id": 9, 325 | "taker_user_id": "9", 326 | "provider_user_id": "8", 327 | "size": 2732008989, 328 | "price": 1000000, 329 | "provider_side": 1, 330 | "timestamp": 1317086239.693123 331 | } 332 | }, 333 | { 334 | "type": "order_status", 335 | "timestamp": 1317086239.693123, 336 | "product_id": 0, 337 | "seq": 23, 338 | "payload": { 339 | "order_id": 9, 340 | "status": "done", 341 | "size": 0, 342 | "user_id": "8", 343 | "reason": "filled", 344 | "timestamp": 1317086239.693123 345 | } 346 | }, 347 | { 348 | "type": "match", 349 | "timestamp": 1317086239.693123, 350 | "product_id": 0, 351 | "seq": 24, 352 | "payload": { 353 | "taker_id": 9, 354 | "provider_id": 3, 355 | "taker_user_id": "9", 356 | "provider_user_id": "6", 357 | "size": 120000000, 358 | "price": 1100000000, 359 | "provider_side": 1, 360 | "timestamp": 1317086239.693123 361 | } 362 | }, 363 | { 364 | "type": "order_status", 365 | "timestamp": 1317086239.693123, 366 | "product_id": 0, 367 | "seq": 25, 368 | "payload": { 369 | "order_id": 3, 370 | "status": "done", 371 | "size": 0, 372 | "user_id": "6", 373 | "reason": "filled", 374 | "timestamp": 1317086239.693123 375 | } 376 | }, 377 | { 378 | "type": "match", 379 | "timestamp": 1317086239.693123, 380 | "product_id": 0, 381 | "seq": 26, 382 | "payload": { 383 | "taker_id": 9, 384 | "provider_id": 4, 385 | "taker_user_id": "9", 386 | "provider_user_id": "7", 387 | "size": 210000000, 388 | "price": 1100000000, 389 | "provider_side": 1, 390 | "timestamp": 1317086239.693123 391 | } 392 | }, 393 | { 394 | "type": "order_status", 395 | "timestamp": 1317086239.693123, 396 | "product_id": 0, 397 | "seq": 27, 398 | "payload": { 399 | "order_id": 4, 400 | "status": "done", 401 | "size": 0, 402 | "user_id": "7", 403 | "reason": "filled", 404 | "timestamp": 1317086239.693123 405 | } 406 | }, 407 | { 408 | "type": "match", 409 | "timestamp": 1317086239.693123, 410 | "product_id": 0, 411 | "seq": 28, 412 | "payload": { 413 | "taker_id": 9, 414 | "provider_id": 5, 415 | "taker_user_id": "9", 416 | "provider_user_id": "5", 417 | "size": 8056912305, 418 | "price": 1800000000, 419 | "provider_side": 1, 420 | "timestamp": 1317086239.693123 421 | } 422 | }, 423 | { 424 | "type": "order_status", 425 | "timestamp": 1317086239.693123, 426 | "product_id": 0, 427 | "seq": 29, 428 | "payload": { 429 | "order_id": 5, 430 | "status": "done", 431 | "size": 0, 432 | "user_id": "5", 433 | "reason": "filled", 434 | "timestamp": 1317086239.693123 435 | } 436 | }, 437 | { 438 | "type": "order_status", 439 | "timestamp": 1317086239.693123, 440 | "product_id": 0, 441 | "seq": 30, 442 | "payload": { 443 | "status": "open", 444 | "side": 0, 445 | "order_id": 9, 446 | "user_id": "9", 447 | "price": 10000000000, 448 | "size": 8911078706, 449 | "timestamp": 1317086239.693123 450 | } 451 | } 452 | ] -------------------------------------------------------------------------------- /test/unit/match_over/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794529249, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": "2", 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794529249, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": "3", 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794529249, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": "4", 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794529250, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1800000000, 41 | "user_id": "5", 42 | "side": 1, 43 | "size": 8056912305 44 | } 45 | }, 46 | { 47 | "timestamp": 1311794529250, 48 | "type": "order", 49 | "payload": { 50 | "order_id": 3, 51 | "price": 1100000000, 52 | "user_id": "6", 53 | "side": 1, 54 | "size": 120000000 55 | } 56 | }, 57 | { 58 | "timestamp": 1311794529250, 59 | "type": "order", 60 | "payload": { 61 | "order_id": 4, 62 | "price": 1100000000, 63 | "user_id": "7", 64 | "side": 1, 65 | "size": 210000000 66 | } 67 | }, 68 | { 69 | "timestamp": 1311794529250, 70 | "type": "order", 71 | "payload": { 72 | "order_id": 9, 73 | "price": 1000000, 74 | "user_id": "8", 75 | "side": 1, 76 | "size": 10010000000 77 | } 78 | }, 79 | { 80 | "timestamp": 1311794529250, 81 | "type": "order", 82 | "payload": { 83 | "order_id": 9, 84 | "price": 10000000000, 85 | "user_id": "9", 86 | "side": 0, 87 | "size": 20030000000 88 | } 89 | } 90 | ] 91 | -------------------------------------------------------------------------------- /test/unit/match_over/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1023000000, 6 | "size": 5234591011, 7 | "order_id": 0, 8 | "user_id": "2" 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1024000000, 21 | "size": 1023400000, 22 | "order_id": 1, 23 | "user_id": "3" 24 | }, 25 | { 26 | "price": 1023000000, 27 | "size": 5234591011, 28 | "order_id": 0, 29 | "user_id": "2" 30 | } 31 | ], 32 | "asks": [], 33 | "state_num": 2, 34 | "output_seq": 5, 35 | "ticker": {}, 36 | "recovery_journal": "matcher_in.0.1317086239.log" 37 | }, 38 | { 39 | "bids": [ 40 | { 41 | "price": 1024000000, 42 | "size": 1023400000, 43 | "order_id": 1, 44 | "user_id": "3" 45 | }, 46 | { 47 | "price": 1023000000, 48 | "size": 5234591011, 49 | "order_id": 0, 50 | "user_id": "2" 51 | }, 52 | { 53 | "price": 1023000000, 54 | "size": 1020000000, 55 | "order_id": 2, 56 | "user_id": "4" 57 | } 58 | ], 59 | "asks": [], 60 | "state_num": 2, 61 | "output_seq": 7, 62 | "ticker": {}, 63 | "recovery_journal": "matcher_in.0.1317086239.log" 64 | }, 65 | { 66 | "bids": [ 67 | { 68 | "price": 1024000000, 69 | "size": 1023400000, 70 | "order_id": 1, 71 | "user_id": "3" 72 | }, 73 | { 74 | "price": 1023000000, 75 | "size": 5234591011, 76 | "order_id": 0, 77 | "user_id": "2" 78 | }, 79 | { 80 | "price": 1023000000, 81 | "size": 1020000000, 82 | "order_id": 2, 83 | "user_id": "4" 84 | } 85 | ], 86 | "asks": [ 87 | { 88 | "price": 1800000000, 89 | "size": 8056912305, 90 | "order_id": 5, 91 | "user_id": "5" 92 | } 93 | ], 94 | "state_num": 2, 95 | "output_seq": 9, 96 | "ticker": {}, 97 | "recovery_journal": "matcher_in.0.1317086239.log" 98 | }, 99 | { 100 | "bids": [ 101 | { 102 | "price": 1024000000, 103 | "size": 1023400000, 104 | "order_id": 1, 105 | "user_id": "3" 106 | }, 107 | { 108 | "price": 1023000000, 109 | "size": 5234591011, 110 | "order_id": 0, 111 | "user_id": "2" 112 | }, 113 | { 114 | "price": 1023000000, 115 | "size": 1020000000, 116 | "order_id": 2, 117 | "user_id": "4" 118 | } 119 | ], 120 | "asks": [ 121 | { 122 | "price": 1100000000, 123 | "size": 120000000, 124 | "order_id": 3, 125 | "user_id": "6" 126 | }, 127 | { 128 | "price": 1800000000, 129 | "size": 8056912305, 130 | "order_id": 5, 131 | "user_id": "5" 132 | } 133 | ], 134 | "state_num": 2, 135 | "output_seq": 11, 136 | "ticker": {}, 137 | "recovery_journal": "matcher_in.0.1317086239.log" 138 | }, 139 | { 140 | "bids": [ 141 | { 142 | "price": 1024000000, 143 | "size": 1023400000, 144 | "order_id": 1, 145 | "user_id": "3" 146 | }, 147 | { 148 | "price": 1023000000, 149 | "size": 5234591011, 150 | "order_id": 0, 151 | "user_id": "2" 152 | }, 153 | { 154 | "price": 1023000000, 155 | "size": 1020000000, 156 | "order_id": 2, 157 | "user_id": "4" 158 | } 159 | ], 160 | "asks": [ 161 | { 162 | "price": 1100000000, 163 | "size": 120000000, 164 | "order_id": 3, 165 | "user_id": "6" 166 | }, 167 | { 168 | "price": 1100000000, 169 | "size": 210000000, 170 | "order_id": 4, 171 | "user_id": "7" 172 | }, 173 | { 174 | "price": 1800000000, 175 | "size": 8056912305, 176 | "order_id": 5, 177 | "user_id": "5" 178 | } 179 | ], 180 | "state_num": 2, 181 | "output_seq": 13, 182 | "ticker": {}, 183 | "recovery_journal": "matcher_in.0.1317086239.log" 184 | }, 185 | { 186 | "bids": [], 187 | "asks": [ 188 | { 189 | "price": 1000000, 190 | "size": 2732008989, 191 | "order_id": 9, 192 | "user_id": "8" 193 | }, 194 | { 195 | "price": 1100000000, 196 | "size": 120000000, 197 | "order_id": 3, 198 | "user_id": "6" 199 | }, 200 | { 201 | "price": 1100000000, 202 | "size": 210000000, 203 | "order_id": 4, 204 | "user_id": "7" 205 | }, 206 | { 207 | "price": 1800000000, 208 | "size": 8056912305, 209 | "order_id": 5, 210 | "user_id": "5" 211 | } 212 | ], 213 | "state_num": 2, 214 | "output_seq": 21, 215 | "ticker": { 216 | "price": 1023000000, 217 | "size": 1020000000, 218 | "timestamp": 1317086239.693123 219 | }, 220 | "recovery_journal": "matcher_in.0.1317086239.log" 221 | }, 222 | { 223 | "bids": [ 224 | { 225 | "price": 10000000000, 226 | "size": 8911078706, 227 | "order_id": 9, 228 | "user_id": "9" 229 | } 230 | ], 231 | "asks": [], 232 | "state_num": 2, 233 | "output_seq": 31, 234 | "ticker": { 235 | "price": 1800000000, 236 | "size": 8056912305, 237 | "timestamp": 1317086239.693123 238 | }, 239 | "recovery_journal": "matcher_in.0.1317086239.log" 240 | } 241 | ] -------------------------------------------------------------------------------- /test/unit/match_partial/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1023000000,"user_id":"1","side":0,"size":5234591011},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1024000000,"user_id":"2","side":0,"size":1023400000},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":2,"price":1023000000,"user_id":"3","side":0,"size":1020000000},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":5,"price":1800000000,"user_id":"4","side":1,"size":8056912305},"seq":4} 6 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":3,"price":1100000000,"user_id":"5","side":1,"size":120000000},"seq":5} 7 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":4,"price":1100000000,"user_id":"6","side":1,"size":210000000},"seq":6} 8 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":6,"price":1059000000,"user_id":"7","side":0,"size":110000000},"seq":7} 9 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":7,"price":1100000000,"user_id":"8","side":0,"size":20000000},"seq":8} 10 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":8,"price":1101000000,"user_id":"9","side":0,"size":20000000},"seq":9} 11 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":9,"price":1059000000,"user_id":"10","side":1,"size":20000000},"seq":10} 12 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":10,"price":1058000000,"user_id":"11","side":1,"size":20000000},"seq":11} 13 | {"type":"state","payload":1} 14 | -------------------------------------------------------------------------------- /test/unit/match_partial/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state", 4 | "payload": { 5 | "bids": [ 6 | { 7 | "price": 1059000000, 8 | "size": 70000000, 9 | "order_id": 6, 10 | "user_id": "7" 11 | }, 12 | { 13 | "price": 1024000000, 14 | "size": 1023400000, 15 | "order_id": 1, 16 | "user_id": "2" 17 | }, 18 | { 19 | "price": 1023000000, 20 | "size": 5234591011, 21 | "order_id": 0, 22 | "user_id": "1" 23 | }, 24 | { 25 | "price": 1023000000, 26 | "size": 1020000000, 27 | "order_id": 2, 28 | "user_id": "3" 29 | } 30 | ], 31 | "asks": [ 32 | { 33 | "price": 1100000000, 34 | "size": 80000000, 35 | "order_id": 3, 36 | "user_id": "5" 37 | }, 38 | { 39 | "price": 1100000000, 40 | "size": 210000000, 41 | "order_id": 4, 42 | "user_id": "6" 43 | }, 44 | { 45 | "price": 1800000000, 46 | "size": 8056912305, 47 | "order_id": 5, 48 | "user_id": "4" 49 | } 50 | ], 51 | "state_num": 2, 52 | "output_seq": 27, 53 | "ticker": { 54 | "price": 1059000000, 55 | "size": 20000000, 56 | "timestamp": 1317086239.693123 57 | }, 58 | "recovery_journal": "matcher_in.0.1317086239.log" 59 | }, 60 | "timestamp": 1317086239.693123, 61 | "seq": 1 62 | } 63 | ] -------------------------------------------------------------------------------- /test/unit/match_partial/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": "1", 12 | "price": 1023000000, 13 | "size": 5234591011, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": "1", 27 | "price": 1023000000, 28 | "size": 5234591011, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 0, 40 | "order_id": 1, 41 | "user_id": "2", 42 | "price": 1024000000, 43 | "size": 1023400000, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "order_status", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "status": "open", 54 | "side": 0, 55 | "order_id": 1, 56 | "user_id": "2", 57 | "price": 1024000000, 58 | "size": 1023400000, 59 | "timestamp": 1317086239.693123 60 | } 61 | }, 62 | { 63 | "type": "order_status", 64 | "timestamp": 1317086239.693123, 65 | "product_id": 0, 66 | "seq": 5, 67 | "payload": { 68 | "status": "received", 69 | "side": 0, 70 | "order_id": 2, 71 | "user_id": "3", 72 | "price": 1023000000, 73 | "size": 1020000000, 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "status": "open", 84 | "side": 0, 85 | "order_id": 2, 86 | "user_id": "3", 87 | "price": 1023000000, 88 | "size": 1020000000, 89 | "timestamp": 1317086239.693123 90 | } 91 | }, 92 | { 93 | "type": "order_status", 94 | "timestamp": 1317086239.693123, 95 | "product_id": 0, 96 | "seq": 7, 97 | "payload": { 98 | "status": "received", 99 | "side": 1, 100 | "order_id": 5, 101 | "user_id": "4", 102 | "price": 1800000000, 103 | "size": 8056912305, 104 | "timestamp": 1317086239.693123 105 | } 106 | }, 107 | { 108 | "type": "order_status", 109 | "timestamp": 1317086239.693123, 110 | "product_id": 0, 111 | "seq": 8, 112 | "payload": { 113 | "status": "open", 114 | "side": 1, 115 | "order_id": 5, 116 | "user_id": "4", 117 | "price": 1800000000, 118 | "size": 8056912305, 119 | "timestamp": 1317086239.693123 120 | } 121 | }, 122 | { 123 | "type": "order_status", 124 | "timestamp": 1317086239.693123, 125 | "product_id": 0, 126 | "seq": 9, 127 | "payload": { 128 | "status": "received", 129 | "side": 1, 130 | "order_id": 3, 131 | "user_id": "5", 132 | "price": 1100000000, 133 | "size": 120000000, 134 | "timestamp": 1317086239.693123 135 | } 136 | }, 137 | { 138 | "type": "order_status", 139 | "timestamp": 1317086239.693123, 140 | "product_id": 0, 141 | "seq": 10, 142 | "payload": { 143 | "status": "open", 144 | "side": 1, 145 | "order_id": 3, 146 | "user_id": "5", 147 | "price": 1100000000, 148 | "size": 120000000, 149 | "timestamp": 1317086239.693123 150 | } 151 | }, 152 | { 153 | "type": "order_status", 154 | "timestamp": 1317086239.693123, 155 | "product_id": 0, 156 | "seq": 11, 157 | "payload": { 158 | "status": "received", 159 | "side": 1, 160 | "order_id": 4, 161 | "user_id": "6", 162 | "price": 1100000000, 163 | "size": 210000000, 164 | "timestamp": 1317086239.693123 165 | } 166 | }, 167 | { 168 | "type": "order_status", 169 | "timestamp": 1317086239.693123, 170 | "product_id": 0, 171 | "seq": 12, 172 | "payload": { 173 | "status": "open", 174 | "side": 1, 175 | "order_id": 4, 176 | "user_id": "6", 177 | "price": 1100000000, 178 | "size": 210000000, 179 | "timestamp": 1317086239.693123 180 | } 181 | }, 182 | { 183 | "type": "order_status", 184 | "timestamp": 1317086239.693123, 185 | "product_id": 0, 186 | "seq": 13, 187 | "payload": { 188 | "status": "received", 189 | "side": 0, 190 | "order_id": 6, 191 | "user_id": "7", 192 | "price": 1059000000, 193 | "size": 110000000, 194 | "timestamp": 1317086239.693123 195 | } 196 | }, 197 | { 198 | "type": "order_status", 199 | "timestamp": 1317086239.693123, 200 | "product_id": 0, 201 | "seq": 14, 202 | "payload": { 203 | "status": "open", 204 | "side": 0, 205 | "order_id": 6, 206 | "user_id": "7", 207 | "price": 1059000000, 208 | "size": 110000000, 209 | "timestamp": 1317086239.693123 210 | } 211 | }, 212 | { 213 | "type": "order_status", 214 | "timestamp": 1317086239.693123, 215 | "product_id": 0, 216 | "seq": 15, 217 | "payload": { 218 | "status": "received", 219 | "side": 0, 220 | "order_id": 7, 221 | "user_id": "8", 222 | "price": 1100000000, 223 | "size": 20000000, 224 | "timestamp": 1317086239.693123 225 | } 226 | }, 227 | { 228 | "type": "match", 229 | "timestamp": 1317086239.693123, 230 | "product_id": 0, 231 | "seq": 16, 232 | "payload": { 233 | "taker_id": 7, 234 | "provider_id": 3, 235 | "taker_user_id": "8", 236 | "provider_user_id": "5", 237 | "size": 20000000, 238 | "price": 1100000000, 239 | "provider_side": 1, 240 | "timestamp": 1317086239.693123 241 | } 242 | }, 243 | { 244 | "type": "order_status", 245 | "timestamp": 1317086239.693123, 246 | "product_id": 0, 247 | "seq": 17, 248 | "payload": { 249 | "order_id": 7, 250 | "status": "done", 251 | "size": 0, 252 | "user_id": "8", 253 | "reason": "filled", 254 | "timestamp": 1317086239.693123 255 | } 256 | }, 257 | { 258 | "type": "order_status", 259 | "timestamp": 1317086239.693123, 260 | "product_id": 0, 261 | "seq": 18, 262 | "payload": { 263 | "status": "received", 264 | "side": 0, 265 | "order_id": 8, 266 | "user_id": "9", 267 | "price": 1101000000, 268 | "size": 20000000, 269 | "timestamp": 1317086239.693123 270 | } 271 | }, 272 | { 273 | "type": "match", 274 | "timestamp": 1317086239.693123, 275 | "product_id": 0, 276 | "seq": 19, 277 | "payload": { 278 | "taker_id": 8, 279 | "provider_id": 3, 280 | "taker_user_id": "9", 281 | "provider_user_id": "5", 282 | "size": 20000000, 283 | "price": 1100000000, 284 | "provider_side": 1, 285 | "timestamp": 1317086239.693123 286 | } 287 | }, 288 | { 289 | "type": "order_status", 290 | "timestamp": 1317086239.693123, 291 | "product_id": 0, 292 | "seq": 20, 293 | "payload": { 294 | "order_id": 8, 295 | "status": "done", 296 | "size": 0, 297 | "user_id": "9", 298 | "reason": "filled", 299 | "timestamp": 1317086239.693123 300 | } 301 | }, 302 | { 303 | "type": "order_status", 304 | "timestamp": 1317086239.693123, 305 | "product_id": 0, 306 | "seq": 21, 307 | "payload": { 308 | "status": "received", 309 | "side": 1, 310 | "order_id": 9, 311 | "user_id": "10", 312 | "price": 1059000000, 313 | "size": 20000000, 314 | "timestamp": 1317086239.693123 315 | } 316 | }, 317 | { 318 | "type": "match", 319 | "timestamp": 1317086239.693123, 320 | "product_id": 0, 321 | "seq": 22, 322 | "payload": { 323 | "taker_id": 9, 324 | "provider_id": 6, 325 | "taker_user_id": "10", 326 | "provider_user_id": "7", 327 | "size": 20000000, 328 | "price": 1059000000, 329 | "provider_side": 0, 330 | "timestamp": 1317086239.693123 331 | } 332 | }, 333 | { 334 | "type": "order_status", 335 | "timestamp": 1317086239.693123, 336 | "product_id": 0, 337 | "seq": 23, 338 | "payload": { 339 | "order_id": 9, 340 | "status": "done", 341 | "size": 0, 342 | "user_id": "10", 343 | "reason": "filled", 344 | "timestamp": 1317086239.693123 345 | } 346 | }, 347 | { 348 | "type": "order_status", 349 | "timestamp": 1317086239.693123, 350 | "product_id": 0, 351 | "seq": 24, 352 | "payload": { 353 | "status": "received", 354 | "side": 1, 355 | "order_id": 10, 356 | "user_id": "11", 357 | "price": 1058000000, 358 | "size": 20000000, 359 | "timestamp": 1317086239.693123 360 | } 361 | }, 362 | { 363 | "type": "match", 364 | "timestamp": 1317086239.693123, 365 | "product_id": 0, 366 | "seq": 25, 367 | "payload": { 368 | "taker_id": 10, 369 | "provider_id": 6, 370 | "taker_user_id": "11", 371 | "provider_user_id": "7", 372 | "size": 20000000, 373 | "price": 1059000000, 374 | "provider_side": 0, 375 | "timestamp": 1317086239.693123 376 | } 377 | }, 378 | { 379 | "type": "order_status", 380 | "timestamp": 1317086239.693123, 381 | "product_id": 0, 382 | "seq": 26, 383 | "payload": { 384 | "order_id": 10, 385 | "status": "done", 386 | "size": 0, 387 | "user_id": "11", 388 | "reason": "filled", 389 | "timestamp": 1317086239.693123 390 | } 391 | } 392 | ] -------------------------------------------------------------------------------- /test/unit/match_partial/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794535313, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": "1", 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794535313, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": "2", 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794535313, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": "3", 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794535313, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1800000000, 41 | "user_id": "4", 42 | "side": 1, 43 | "size": 8056912305 44 | } 45 | }, 46 | { 47 | "timestamp": 1311794535314, 48 | "type": "order", 49 | "payload": { 50 | "order_id": 3, 51 | "price": 1100000000, 52 | "user_id": "5", 53 | "side": 1, 54 | "size": 120000000 55 | } 56 | }, 57 | { 58 | "timestamp": 1311794535314, 59 | "type": "order", 60 | "payload": { 61 | "order_id": 4, 62 | "price": 1100000000, 63 | "user_id": "6", 64 | "side": 1, 65 | "size": 210000000 66 | } 67 | }, 68 | { 69 | "timestamp": 1311794535314, 70 | "type": "order", 71 | "payload": { 72 | "order_id": 6, 73 | "price": 1059000000, 74 | "user_id": "7", 75 | "side": 0, 76 | "size": 110000000 77 | } 78 | }, 79 | { 80 | "timestamp": 1311794535314, 81 | "type": "order", 82 | "payload": { 83 | "order_id": 7, 84 | "price": 1100000000, 85 | "user_id": "8", 86 | "side": 0, 87 | "size": 20000000 88 | } 89 | }, 90 | { 91 | "timestamp": 1311794535314, 92 | "type": "order", 93 | "payload": { 94 | "order_id": 8, 95 | "price": 1101000000, 96 | "user_id": "9", 97 | "side": 0, 98 | "size": 20000000 99 | } 100 | }, 101 | { 102 | "timestamp": 1311794535314, 103 | "type": "order", 104 | "payload": { 105 | "order_id": 9, 106 | "price": 1059000000, 107 | "user_id": "10", 108 | "side": 1, 109 | "size": 20000000 110 | } 111 | }, 112 | { 113 | "timestamp": 1311794535314, 114 | "type": "order", 115 | "payload": { 116 | "order_id": 10, 117 | "price": 1058000000, 118 | "user_id": "11", 119 | "side": 1, 120 | "size": 20000000 121 | } 122 | } 123 | ] 124 | -------------------------------------------------------------------------------- /test/unit/match_partial/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1023000000, 6 | "size": 5234591011, 7 | "order_id": 0, 8 | "user_id": "1" 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1024000000, 21 | "size": 1023400000, 22 | "order_id": 1, 23 | "user_id": "2" 24 | }, 25 | { 26 | "price": 1023000000, 27 | "size": 5234591011, 28 | "order_id": 0, 29 | "user_id": "1" 30 | } 31 | ], 32 | "asks": [], 33 | "state_num": 2, 34 | "output_seq": 5, 35 | "ticker": {}, 36 | "recovery_journal": "matcher_in.0.1317086239.log" 37 | }, 38 | { 39 | "bids": [ 40 | { 41 | "price": 1024000000, 42 | "size": 1023400000, 43 | "order_id": 1, 44 | "user_id": "2" 45 | }, 46 | { 47 | "price": 1023000000, 48 | "size": 5234591011, 49 | "order_id": 0, 50 | "user_id": "1" 51 | }, 52 | { 53 | "price": 1023000000, 54 | "size": 1020000000, 55 | "order_id": 2, 56 | "user_id": "3" 57 | } 58 | ], 59 | "asks": [], 60 | "state_num": 2, 61 | "output_seq": 7, 62 | "ticker": {}, 63 | "recovery_journal": "matcher_in.0.1317086239.log" 64 | }, 65 | { 66 | "bids": [ 67 | { 68 | "price": 1024000000, 69 | "size": 1023400000, 70 | "order_id": 1, 71 | "user_id": "2" 72 | }, 73 | { 74 | "price": 1023000000, 75 | "size": 5234591011, 76 | "order_id": 0, 77 | "user_id": "1" 78 | }, 79 | { 80 | "price": 1023000000, 81 | "size": 1020000000, 82 | "order_id": 2, 83 | "user_id": "3" 84 | } 85 | ], 86 | "asks": [ 87 | { 88 | "price": 1800000000, 89 | "size": 8056912305, 90 | "order_id": 5, 91 | "user_id": "4" 92 | } 93 | ], 94 | "state_num": 2, 95 | "output_seq": 9, 96 | "ticker": {}, 97 | "recovery_journal": "matcher_in.0.1317086239.log" 98 | }, 99 | { 100 | "bids": [ 101 | { 102 | "price": 1024000000, 103 | "size": 1023400000, 104 | "order_id": 1, 105 | "user_id": "2" 106 | }, 107 | { 108 | "price": 1023000000, 109 | "size": 5234591011, 110 | "order_id": 0, 111 | "user_id": "1" 112 | }, 113 | { 114 | "price": 1023000000, 115 | "size": 1020000000, 116 | "order_id": 2, 117 | "user_id": "3" 118 | } 119 | ], 120 | "asks": [ 121 | { 122 | "price": 1100000000, 123 | "size": 120000000, 124 | "order_id": 3, 125 | "user_id": "5" 126 | }, 127 | { 128 | "price": 1800000000, 129 | "size": 8056912305, 130 | "order_id": 5, 131 | "user_id": "4" 132 | } 133 | ], 134 | "state_num": 2, 135 | "output_seq": 11, 136 | "ticker": {}, 137 | "recovery_journal": "matcher_in.0.1317086239.log" 138 | }, 139 | { 140 | "bids": [ 141 | { 142 | "price": 1024000000, 143 | "size": 1023400000, 144 | "order_id": 1, 145 | "user_id": "2" 146 | }, 147 | { 148 | "price": 1023000000, 149 | "size": 5234591011, 150 | "order_id": 0, 151 | "user_id": "1" 152 | }, 153 | { 154 | "price": 1023000000, 155 | "size": 1020000000, 156 | "order_id": 2, 157 | "user_id": "3" 158 | } 159 | ], 160 | "asks": [ 161 | { 162 | "price": 1100000000, 163 | "size": 120000000, 164 | "order_id": 3, 165 | "user_id": "5" 166 | }, 167 | { 168 | "price": 1100000000, 169 | "size": 210000000, 170 | "order_id": 4, 171 | "user_id": "6" 172 | }, 173 | { 174 | "price": 1800000000, 175 | "size": 8056912305, 176 | "order_id": 5, 177 | "user_id": "4" 178 | } 179 | ], 180 | "state_num": 2, 181 | "output_seq": 13, 182 | "ticker": {}, 183 | "recovery_journal": "matcher_in.0.1317086239.log" 184 | }, 185 | { 186 | "bids": [ 187 | { 188 | "price": 1059000000, 189 | "size": 110000000, 190 | "order_id": 6, 191 | "user_id": "7" 192 | }, 193 | { 194 | "price": 1024000000, 195 | "size": 1023400000, 196 | "order_id": 1, 197 | "user_id": "2" 198 | }, 199 | { 200 | "price": 1023000000, 201 | "size": 5234591011, 202 | "order_id": 0, 203 | "user_id": "1" 204 | }, 205 | { 206 | "price": 1023000000, 207 | "size": 1020000000, 208 | "order_id": 2, 209 | "user_id": "3" 210 | } 211 | ], 212 | "asks": [ 213 | { 214 | "price": 1100000000, 215 | "size": 120000000, 216 | "order_id": 3, 217 | "user_id": "5" 218 | }, 219 | { 220 | "price": 1100000000, 221 | "size": 210000000, 222 | "order_id": 4, 223 | "user_id": "6" 224 | }, 225 | { 226 | "price": 1800000000, 227 | "size": 8056912305, 228 | "order_id": 5, 229 | "user_id": "4" 230 | } 231 | ], 232 | "state_num": 2, 233 | "output_seq": 15, 234 | "ticker": {}, 235 | "recovery_journal": "matcher_in.0.1317086239.log" 236 | }, 237 | { 238 | "bids": [ 239 | { 240 | "price": 1059000000, 241 | "size": 110000000, 242 | "order_id": 6, 243 | "user_id": "7" 244 | }, 245 | { 246 | "price": 1024000000, 247 | "size": 1023400000, 248 | "order_id": 1, 249 | "user_id": "2" 250 | }, 251 | { 252 | "price": 1023000000, 253 | "size": 5234591011, 254 | "order_id": 0, 255 | "user_id": "1" 256 | }, 257 | { 258 | "price": 1023000000, 259 | "size": 1020000000, 260 | "order_id": 2, 261 | "user_id": "3" 262 | } 263 | ], 264 | "asks": [ 265 | { 266 | "price": 1100000000, 267 | "size": 100000000, 268 | "order_id": 3, 269 | "user_id": "5" 270 | }, 271 | { 272 | "price": 1100000000, 273 | "size": 210000000, 274 | "order_id": 4, 275 | "user_id": "6" 276 | }, 277 | { 278 | "price": 1800000000, 279 | "size": 8056912305, 280 | "order_id": 5, 281 | "user_id": "4" 282 | } 283 | ], 284 | "state_num": 2, 285 | "output_seq": 18, 286 | "ticker": { 287 | "price": 1100000000, 288 | "size": 20000000, 289 | "timestamp": 1317086239.693123 290 | }, 291 | "recovery_journal": "matcher_in.0.1317086239.log" 292 | }, 293 | { 294 | "bids": [ 295 | { 296 | "price": 1059000000, 297 | "size": 110000000, 298 | "order_id": 6, 299 | "user_id": "7" 300 | }, 301 | { 302 | "price": 1024000000, 303 | "size": 1023400000, 304 | "order_id": 1, 305 | "user_id": "2" 306 | }, 307 | { 308 | "price": 1023000000, 309 | "size": 5234591011, 310 | "order_id": 0, 311 | "user_id": "1" 312 | }, 313 | { 314 | "price": 1023000000, 315 | "size": 1020000000, 316 | "order_id": 2, 317 | "user_id": "3" 318 | } 319 | ], 320 | "asks": [ 321 | { 322 | "price": 1100000000, 323 | "size": 80000000, 324 | "order_id": 3, 325 | "user_id": "5" 326 | }, 327 | { 328 | "price": 1100000000, 329 | "size": 210000000, 330 | "order_id": 4, 331 | "user_id": "6" 332 | }, 333 | { 334 | "price": 1800000000, 335 | "size": 8056912305, 336 | "order_id": 5, 337 | "user_id": "4" 338 | } 339 | ], 340 | "state_num": 2, 341 | "output_seq": 21, 342 | "ticker": { 343 | "price": 1100000000, 344 | "size": 20000000, 345 | "timestamp": 1317086239.693123 346 | }, 347 | "recovery_journal": "matcher_in.0.1317086239.log" 348 | }, 349 | { 350 | "bids": [ 351 | { 352 | "price": 1059000000, 353 | "size": 90000000, 354 | "order_id": 6, 355 | "user_id": "7" 356 | }, 357 | { 358 | "price": 1024000000, 359 | "size": 1023400000, 360 | "order_id": 1, 361 | "user_id": "2" 362 | }, 363 | { 364 | "price": 1023000000, 365 | "size": 5234591011, 366 | "order_id": 0, 367 | "user_id": "1" 368 | }, 369 | { 370 | "price": 1023000000, 371 | "size": 1020000000, 372 | "order_id": 2, 373 | "user_id": "3" 374 | } 375 | ], 376 | "asks": [ 377 | { 378 | "price": 1100000000, 379 | "size": 80000000, 380 | "order_id": 3, 381 | "user_id": "5" 382 | }, 383 | { 384 | "price": 1100000000, 385 | "size": 210000000, 386 | "order_id": 4, 387 | "user_id": "6" 388 | }, 389 | { 390 | "price": 1800000000, 391 | "size": 8056912305, 392 | "order_id": 5, 393 | "user_id": "4" 394 | } 395 | ], 396 | "state_num": 2, 397 | "output_seq": 24, 398 | "ticker": { 399 | "price": 1059000000, 400 | "size": 20000000, 401 | "timestamp": 1317086239.693123 402 | }, 403 | "recovery_journal": "matcher_in.0.1317086239.log" 404 | }, 405 | { 406 | "bids": [ 407 | { 408 | "price": 1059000000, 409 | "size": 70000000, 410 | "order_id": 6, 411 | "user_id": "7" 412 | }, 413 | { 414 | "price": 1024000000, 415 | "size": 1023400000, 416 | "order_id": 1, 417 | "user_id": "2" 418 | }, 419 | { 420 | "price": 1023000000, 421 | "size": 5234591011, 422 | "order_id": 0, 423 | "user_id": "1" 424 | }, 425 | { 426 | "price": 1023000000, 427 | "size": 1020000000, 428 | "order_id": 2, 429 | "user_id": "3" 430 | } 431 | ], 432 | "asks": [ 433 | { 434 | "price": 1100000000, 435 | "size": 80000000, 436 | "order_id": 3, 437 | "user_id": "5" 438 | }, 439 | { 440 | "price": 1100000000, 441 | "size": 210000000, 442 | "order_id": 4, 443 | "user_id": "6" 444 | }, 445 | { 446 | "price": 1800000000, 447 | "size": 8056912305, 448 | "order_id": 5, 449 | "user_id": "4" 450 | } 451 | ], 452 | "state_num": 2, 453 | "output_seq": 27, 454 | "ticker": { 455 | "price": 1059000000, 456 | "size": 20000000, 457 | "timestamp": 1317086239.693123 458 | }, 459 | "recovery_journal": "matcher_in.0.1317086239.log" 460 | } 461 | ] -------------------------------------------------------------------------------- /test/unit/match_several/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1023000000,"user_id":"1","side":0,"size":5234591011},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1024000000,"user_id":"2","side":0,"size":1023400000},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":2,"price":1023000000,"user_id":"3","side":0,"size":1020000000},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":5,"price":1800000000,"user_id":"4","side":1,"size":8056912305},"seq":4} 6 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":3,"price":1100000000,"user_id":"5","side":1,"size":120000000},"seq":5} 7 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":4,"price":1100000000,"user_id":"6","side":1,"size":210000000},"seq":6} 8 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":7,"price":1100000000,"user_id":"7","side":0,"size":220000000},"seq":7} 9 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":9,"price":1023000000,"user_id":"8","side":1,"size":2020000000},"seq":8} 10 | {"type":"state","payload":1} 11 | -------------------------------------------------------------------------------- /test/unit/match_several/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state", 4 | "payload": { 5 | "bids": [ 6 | { 7 | "price": 1023000000, 8 | "size": 4237991011, 9 | "order_id": 0, 10 | "user_id": "1" 11 | }, 12 | { 13 | "price": 1023000000, 14 | "size": 1020000000, 15 | "order_id": 2, 16 | "user_id": "3" 17 | } 18 | ], 19 | "asks": [ 20 | { 21 | "price": 1100000000, 22 | "size": 110000000, 23 | "order_id": 4, 24 | "user_id": "6" 25 | }, 26 | { 27 | "price": 1800000000, 28 | "size": 8056912305, 29 | "order_id": 5, 30 | "user_id": "4" 31 | } 32 | ], 33 | "state_num": 2, 34 | "output_seq": 23, 35 | "ticker": { 36 | "price": 1023000000, 37 | "size": 996600000, 38 | "timestamp": 1317086239.693123 39 | }, 40 | "recovery_journal": "matcher_in.0.1317086239.log" 41 | }, 42 | "timestamp": 1317086239.693123, 43 | "seq": 1 44 | } 45 | ] -------------------------------------------------------------------------------- /test/unit/match_several/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": "1", 12 | "price": 1023000000, 13 | "size": 5234591011, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": "1", 27 | "price": 1023000000, 28 | "size": 5234591011, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 0, 40 | "order_id": 1, 41 | "user_id": "2", 42 | "price": 1024000000, 43 | "size": 1023400000, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "order_status", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "status": "open", 54 | "side": 0, 55 | "order_id": 1, 56 | "user_id": "2", 57 | "price": 1024000000, 58 | "size": 1023400000, 59 | "timestamp": 1317086239.693123 60 | } 61 | }, 62 | { 63 | "type": "order_status", 64 | "timestamp": 1317086239.693123, 65 | "product_id": 0, 66 | "seq": 5, 67 | "payload": { 68 | "status": "received", 69 | "side": 0, 70 | "order_id": 2, 71 | "user_id": "3", 72 | "price": 1023000000, 73 | "size": 1020000000, 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "status": "open", 84 | "side": 0, 85 | "order_id": 2, 86 | "user_id": "3", 87 | "price": 1023000000, 88 | "size": 1020000000, 89 | "timestamp": 1317086239.693123 90 | } 91 | }, 92 | { 93 | "type": "order_status", 94 | "timestamp": 1317086239.693123, 95 | "product_id": 0, 96 | "seq": 7, 97 | "payload": { 98 | "status": "received", 99 | "side": 1, 100 | "order_id": 5, 101 | "user_id": "4", 102 | "price": 1800000000, 103 | "size": 8056912305, 104 | "timestamp": 1317086239.693123 105 | } 106 | }, 107 | { 108 | "type": "order_status", 109 | "timestamp": 1317086239.693123, 110 | "product_id": 0, 111 | "seq": 8, 112 | "payload": { 113 | "status": "open", 114 | "side": 1, 115 | "order_id": 5, 116 | "user_id": "4", 117 | "price": 1800000000, 118 | "size": 8056912305, 119 | "timestamp": 1317086239.693123 120 | } 121 | }, 122 | { 123 | "type": "order_status", 124 | "timestamp": 1317086239.693123, 125 | "product_id": 0, 126 | "seq": 9, 127 | "payload": { 128 | "status": "received", 129 | "side": 1, 130 | "order_id": 3, 131 | "user_id": "5", 132 | "price": 1100000000, 133 | "size": 120000000, 134 | "timestamp": 1317086239.693123 135 | } 136 | }, 137 | { 138 | "type": "order_status", 139 | "timestamp": 1317086239.693123, 140 | "product_id": 0, 141 | "seq": 10, 142 | "payload": { 143 | "status": "open", 144 | "side": 1, 145 | "order_id": 3, 146 | "user_id": "5", 147 | "price": 1100000000, 148 | "size": 120000000, 149 | "timestamp": 1317086239.693123 150 | } 151 | }, 152 | { 153 | "type": "order_status", 154 | "timestamp": 1317086239.693123, 155 | "product_id": 0, 156 | "seq": 11, 157 | "payload": { 158 | "status": "received", 159 | "side": 1, 160 | "order_id": 4, 161 | "user_id": "6", 162 | "price": 1100000000, 163 | "size": 210000000, 164 | "timestamp": 1317086239.693123 165 | } 166 | }, 167 | { 168 | "type": "order_status", 169 | "timestamp": 1317086239.693123, 170 | "product_id": 0, 171 | "seq": 12, 172 | "payload": { 173 | "status": "open", 174 | "side": 1, 175 | "order_id": 4, 176 | "user_id": "6", 177 | "price": 1100000000, 178 | "size": 210000000, 179 | "timestamp": 1317086239.693123 180 | } 181 | }, 182 | { 183 | "type": "order_status", 184 | "timestamp": 1317086239.693123, 185 | "product_id": 0, 186 | "seq": 13, 187 | "payload": { 188 | "status": "received", 189 | "side": 0, 190 | "order_id": 7, 191 | "user_id": "7", 192 | "price": 1100000000, 193 | "size": 220000000, 194 | "timestamp": 1317086239.693123 195 | } 196 | }, 197 | { 198 | "type": "match", 199 | "timestamp": 1317086239.693123, 200 | "product_id": 0, 201 | "seq": 14, 202 | "payload": { 203 | "taker_id": 7, 204 | "provider_id": 3, 205 | "taker_user_id": "7", 206 | "provider_user_id": "5", 207 | "size": 120000000, 208 | "price": 1100000000, 209 | "provider_side": 1, 210 | "timestamp": 1317086239.693123 211 | } 212 | }, 213 | { 214 | "type": "order_status", 215 | "timestamp": 1317086239.693123, 216 | "product_id": 0, 217 | "seq": 15, 218 | "payload": { 219 | "order_id": 3, 220 | "status": "done", 221 | "size": 0, 222 | "user_id": "5", 223 | "reason": "filled", 224 | "timestamp": 1317086239.693123 225 | } 226 | }, 227 | { 228 | "type": "match", 229 | "timestamp": 1317086239.693123, 230 | "product_id": 0, 231 | "seq": 16, 232 | "payload": { 233 | "taker_id": 7, 234 | "provider_id": 4, 235 | "taker_user_id": "7", 236 | "provider_user_id": "6", 237 | "size": 100000000, 238 | "price": 1100000000, 239 | "provider_side": 1, 240 | "timestamp": 1317086239.693123 241 | } 242 | }, 243 | { 244 | "type": "order_status", 245 | "timestamp": 1317086239.693123, 246 | "product_id": 0, 247 | "seq": 17, 248 | "payload": { 249 | "order_id": 7, 250 | "status": "done", 251 | "size": 0, 252 | "user_id": "7", 253 | "reason": "filled", 254 | "timestamp": 1317086239.693123 255 | } 256 | }, 257 | { 258 | "type": "order_status", 259 | "timestamp": 1317086239.693123, 260 | "product_id": 0, 261 | "seq": 18, 262 | "payload": { 263 | "status": "received", 264 | "side": 1, 265 | "order_id": 9, 266 | "user_id": "8", 267 | "price": 1023000000, 268 | "size": 2020000000, 269 | "timestamp": 1317086239.693123 270 | } 271 | }, 272 | { 273 | "type": "match", 274 | "timestamp": 1317086239.693123, 275 | "product_id": 0, 276 | "seq": 19, 277 | "payload": { 278 | "taker_id": 9, 279 | "provider_id": 1, 280 | "taker_user_id": "8", 281 | "provider_user_id": "2", 282 | "size": 1023400000, 283 | "price": 1024000000, 284 | "provider_side": 0, 285 | "timestamp": 1317086239.693123 286 | } 287 | }, 288 | { 289 | "type": "order_status", 290 | "timestamp": 1317086239.693123, 291 | "product_id": 0, 292 | "seq": 20, 293 | "payload": { 294 | "order_id": 1, 295 | "status": "done", 296 | "size": 0, 297 | "user_id": "2", 298 | "reason": "filled", 299 | "timestamp": 1317086239.693123 300 | } 301 | }, 302 | { 303 | "type": "match", 304 | "timestamp": 1317086239.693123, 305 | "product_id": 0, 306 | "seq": 21, 307 | "payload": { 308 | "taker_id": 9, 309 | "provider_id": 0, 310 | "taker_user_id": "8", 311 | "provider_user_id": "1", 312 | "size": 996600000, 313 | "price": 1023000000, 314 | "provider_side": 0, 315 | "timestamp": 1317086239.693123 316 | } 317 | }, 318 | { 319 | "type": "order_status", 320 | "timestamp": 1317086239.693123, 321 | "product_id": 0, 322 | "seq": 22, 323 | "payload": { 324 | "order_id": 9, 325 | "status": "done", 326 | "size": 0, 327 | "user_id": "8", 328 | "reason": "filled", 329 | "timestamp": 1317086239.693123 330 | } 331 | } 332 | ] -------------------------------------------------------------------------------- /test/unit/match_several/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794549952, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1023000000, 8 | "user_id": "1", 9 | "side": 0, 10 | "size": 5234591011 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794549952, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": "2", 20 | "side": 0, 21 | "size": 1023400000 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794549952, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": "3", 31 | "side": 0, 32 | "size": 1020000000 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794549952, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1800000000, 41 | "user_id": "4", 42 | "side": 1, 43 | "size": 8056912305 44 | } 45 | }, 46 | { 47 | "timestamp": 1311794549952, 48 | "type": "order", 49 | "payload": { 50 | "order_id": 3, 51 | "price": 1100000000, 52 | "user_id": "5", 53 | "side": 1, 54 | "size": 120000000 55 | } 56 | }, 57 | { 58 | "timestamp": 1311794549952, 59 | "type": "order", 60 | "payload": { 61 | "order_id": 4, 62 | "price": 1100000000, 63 | "user_id": "6", 64 | "side": 1, 65 | "size": 210000000 66 | } 67 | }, 68 | { 69 | "timestamp": 1311794549953, 70 | "type": "order", 71 | "payload": { 72 | "order_id": 7, 73 | "price": 1100000000, 74 | "user_id": "7", 75 | "side": 0, 76 | "size": 220000000 77 | } 78 | }, 79 | { 80 | "timestamp": 1311794549953, 81 | "type": "order", 82 | "payload": { 83 | "order_id": 9, 84 | "price": 1023000000, 85 | "user_id": "8", 86 | "side": 1, 87 | "size": 2020000000 88 | } 89 | } 90 | ] 91 | -------------------------------------------------------------------------------- /test/unit/match_several/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1023000000, 6 | "size": 5234591011, 7 | "order_id": 0, 8 | "user_id": "1" 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1024000000, 21 | "size": 1023400000, 22 | "order_id": 1, 23 | "user_id": "2" 24 | }, 25 | { 26 | "price": 1023000000, 27 | "size": 5234591011, 28 | "order_id": 0, 29 | "user_id": "1" 30 | } 31 | ], 32 | "asks": [], 33 | "state_num": 2, 34 | "output_seq": 5, 35 | "ticker": {}, 36 | "recovery_journal": "matcher_in.0.1317086239.log" 37 | }, 38 | { 39 | "bids": [ 40 | { 41 | "price": 1024000000, 42 | "size": 1023400000, 43 | "order_id": 1, 44 | "user_id": "2" 45 | }, 46 | { 47 | "price": 1023000000, 48 | "size": 5234591011, 49 | "order_id": 0, 50 | "user_id": "1" 51 | }, 52 | { 53 | "price": 1023000000, 54 | "size": 1020000000, 55 | "order_id": 2, 56 | "user_id": "3" 57 | } 58 | ], 59 | "asks": [], 60 | "state_num": 2, 61 | "output_seq": 7, 62 | "ticker": {}, 63 | "recovery_journal": "matcher_in.0.1317086239.log" 64 | }, 65 | { 66 | "bids": [ 67 | { 68 | "price": 1024000000, 69 | "size": 1023400000, 70 | "order_id": 1, 71 | "user_id": "2" 72 | }, 73 | { 74 | "price": 1023000000, 75 | "size": 5234591011, 76 | "order_id": 0, 77 | "user_id": "1" 78 | }, 79 | { 80 | "price": 1023000000, 81 | "size": 1020000000, 82 | "order_id": 2, 83 | "user_id": "3" 84 | } 85 | ], 86 | "asks": [ 87 | { 88 | "price": 1800000000, 89 | "size": 8056912305, 90 | "order_id": 5, 91 | "user_id": "4" 92 | } 93 | ], 94 | "state_num": 2, 95 | "output_seq": 9, 96 | "ticker": {}, 97 | "recovery_journal": "matcher_in.0.1317086239.log" 98 | }, 99 | { 100 | "bids": [ 101 | { 102 | "price": 1024000000, 103 | "size": 1023400000, 104 | "order_id": 1, 105 | "user_id": "2" 106 | }, 107 | { 108 | "price": 1023000000, 109 | "size": 5234591011, 110 | "order_id": 0, 111 | "user_id": "1" 112 | }, 113 | { 114 | "price": 1023000000, 115 | "size": 1020000000, 116 | "order_id": 2, 117 | "user_id": "3" 118 | } 119 | ], 120 | "asks": [ 121 | { 122 | "price": 1100000000, 123 | "size": 120000000, 124 | "order_id": 3, 125 | "user_id": "5" 126 | }, 127 | { 128 | "price": 1800000000, 129 | "size": 8056912305, 130 | "order_id": 5, 131 | "user_id": "4" 132 | } 133 | ], 134 | "state_num": 2, 135 | "output_seq": 11, 136 | "ticker": {}, 137 | "recovery_journal": "matcher_in.0.1317086239.log" 138 | }, 139 | { 140 | "bids": [ 141 | { 142 | "price": 1024000000, 143 | "size": 1023400000, 144 | "order_id": 1, 145 | "user_id": "2" 146 | }, 147 | { 148 | "price": 1023000000, 149 | "size": 5234591011, 150 | "order_id": 0, 151 | "user_id": "1" 152 | }, 153 | { 154 | "price": 1023000000, 155 | "size": 1020000000, 156 | "order_id": 2, 157 | "user_id": "3" 158 | } 159 | ], 160 | "asks": [ 161 | { 162 | "price": 1100000000, 163 | "size": 120000000, 164 | "order_id": 3, 165 | "user_id": "5" 166 | }, 167 | { 168 | "price": 1100000000, 169 | "size": 210000000, 170 | "order_id": 4, 171 | "user_id": "6" 172 | }, 173 | { 174 | "price": 1800000000, 175 | "size": 8056912305, 176 | "order_id": 5, 177 | "user_id": "4" 178 | } 179 | ], 180 | "state_num": 2, 181 | "output_seq": 13, 182 | "ticker": {}, 183 | "recovery_journal": "matcher_in.0.1317086239.log" 184 | }, 185 | { 186 | "bids": [ 187 | { 188 | "price": 1024000000, 189 | "size": 1023400000, 190 | "order_id": 1, 191 | "user_id": "2" 192 | }, 193 | { 194 | "price": 1023000000, 195 | "size": 5234591011, 196 | "order_id": 0, 197 | "user_id": "1" 198 | }, 199 | { 200 | "price": 1023000000, 201 | "size": 1020000000, 202 | "order_id": 2, 203 | "user_id": "3" 204 | } 205 | ], 206 | "asks": [ 207 | { 208 | "price": 1100000000, 209 | "size": 110000000, 210 | "order_id": 4, 211 | "user_id": "6" 212 | }, 213 | { 214 | "price": 1800000000, 215 | "size": 8056912305, 216 | "order_id": 5, 217 | "user_id": "4" 218 | } 219 | ], 220 | "state_num": 2, 221 | "output_seq": 18, 222 | "ticker": { 223 | "price": 1100000000, 224 | "size": 100000000, 225 | "timestamp": 1317086239.693123 226 | }, 227 | "recovery_journal": "matcher_in.0.1317086239.log" 228 | }, 229 | { 230 | "bids": [ 231 | { 232 | "price": 1023000000, 233 | "size": 4237991011, 234 | "order_id": 0, 235 | "user_id": "1" 236 | }, 237 | { 238 | "price": 1023000000, 239 | "size": 1020000000, 240 | "order_id": 2, 241 | "user_id": "3" 242 | } 243 | ], 244 | "asks": [ 245 | { 246 | "price": 1100000000, 247 | "size": 110000000, 248 | "order_id": 4, 249 | "user_id": "6" 250 | }, 251 | { 252 | "price": 1800000000, 253 | "size": 8056912305, 254 | "order_id": 5, 255 | "user_id": "4" 256 | } 257 | ], 258 | "state_num": 2, 259 | "output_seq": 23, 260 | "ticker": { 261 | "price": 1023000000, 262 | "size": 996600000, 263 | "timestamp": 1317086239.693123 264 | }, 265 | "recovery_journal": "matcher_in.0.1317086239.log" 266 | } 267 | ] -------------------------------------------------------------------------------- /test/unit/trade_with_self/journal.log: -------------------------------------------------------------------------------- 1 | {"type":"state","payload":0} 2 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":0,"price":1022000000,"user_id":"2","side":0,"size":100},"seq":1} 3 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":1,"price":1024000000,"user_id":"3","side":0,"size":200},"seq":2} 4 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":2,"price":1023000000,"user_id":"4","side":0,"size":500},"seq":3} 5 | {"timestamp":1317086239.693123,"type":"order","payload":{"order_id":5,"price":1000000000,"user_id":"4","side":1,"size":1000},"seq":4} 6 | {"type":"state","payload":1} 7 | -------------------------------------------------------------------------------- /test/unit/trade_with_self/recv.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "state", 4 | "payload": { 5 | "bids": [], 6 | "asks": [ 7 | { 8 | "price": 1000000000, 9 | "size": 700, 10 | "order_id": 5, 11 | "user_id": "4" 12 | } 13 | ], 14 | "state_num": 2, 15 | "output_seq": 14, 16 | "ticker": { 17 | "price": 1022000000, 18 | "size": 100, 19 | "timestamp": 1317086239.693123 20 | }, 21 | "recovery_journal": "matcher_in.0.1317086239.log" 22 | }, 23 | "timestamp": 1317086239.693123, 24 | "seq": 1 25 | } 26 | ] -------------------------------------------------------------------------------- /test/unit/trade_with_self/recv_multi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "order_status", 4 | "timestamp": 1317086239.693123, 5 | "product_id": 0, 6 | "seq": 1, 7 | "payload": { 8 | "status": "received", 9 | "side": 0, 10 | "order_id": 0, 11 | "user_id": "2", 12 | "price": 1022000000, 13 | "size": 100, 14 | "timestamp": 1317086239.693123 15 | } 16 | }, 17 | { 18 | "type": "order_status", 19 | "timestamp": 1317086239.693123, 20 | "product_id": 0, 21 | "seq": 2, 22 | "payload": { 23 | "status": "open", 24 | "side": 0, 25 | "order_id": 0, 26 | "user_id": "2", 27 | "price": 1022000000, 28 | "size": 100, 29 | "timestamp": 1317086239.693123 30 | } 31 | }, 32 | { 33 | "type": "order_status", 34 | "timestamp": 1317086239.693123, 35 | "product_id": 0, 36 | "seq": 3, 37 | "payload": { 38 | "status": "received", 39 | "side": 0, 40 | "order_id": 1, 41 | "user_id": "3", 42 | "price": 1024000000, 43 | "size": 200, 44 | "timestamp": 1317086239.693123 45 | } 46 | }, 47 | { 48 | "type": "order_status", 49 | "timestamp": 1317086239.693123, 50 | "product_id": 0, 51 | "seq": 4, 52 | "payload": { 53 | "status": "open", 54 | "side": 0, 55 | "order_id": 1, 56 | "user_id": "3", 57 | "price": 1024000000, 58 | "size": 200, 59 | "timestamp": 1317086239.693123 60 | } 61 | }, 62 | { 63 | "type": "order_status", 64 | "timestamp": 1317086239.693123, 65 | "product_id": 0, 66 | "seq": 5, 67 | "payload": { 68 | "status": "received", 69 | "side": 0, 70 | "order_id": 2, 71 | "user_id": "4", 72 | "price": 1023000000, 73 | "size": 500, 74 | "timestamp": 1317086239.693123 75 | } 76 | }, 77 | { 78 | "type": "order_status", 79 | "timestamp": 1317086239.693123, 80 | "product_id": 0, 81 | "seq": 6, 82 | "payload": { 83 | "status": "open", 84 | "side": 0, 85 | "order_id": 2, 86 | "user_id": "4", 87 | "price": 1023000000, 88 | "size": 500, 89 | "timestamp": 1317086239.693123 90 | } 91 | }, 92 | { 93 | "type": "order_status", 94 | "timestamp": 1317086239.693123, 95 | "product_id": 0, 96 | "seq": 7, 97 | "payload": { 98 | "status": "received", 99 | "side": 1, 100 | "order_id": 5, 101 | "user_id": "4", 102 | "price": 1000000000, 103 | "size": 1000, 104 | "timestamp": 1317086239.693123 105 | } 106 | }, 107 | { 108 | "type": "match", 109 | "timestamp": 1317086239.693123, 110 | "product_id": 0, 111 | "seq": 8, 112 | "payload": { 113 | "taker_id": 5, 114 | "provider_id": 1, 115 | "taker_user_id": "4", 116 | "provider_user_id": "3", 117 | "size": 200, 118 | "price": 1024000000, 119 | "provider_side": 0, 120 | "timestamp": 1317086239.693123 121 | } 122 | }, 123 | { 124 | "type": "order_status", 125 | "timestamp": 1317086239.693123, 126 | "product_id": 0, 127 | "seq": 9, 128 | "payload": { 129 | "order_id": 1, 130 | "status": "done", 131 | "size": 0, 132 | "user_id": "3", 133 | "reason": "filled", 134 | "timestamp": 1317086239.693123 135 | } 136 | }, 137 | { 138 | "type": "order_status", 139 | "timestamp": 1317086239.693123, 140 | "product_id": 0, 141 | "seq": 10, 142 | "payload": { 143 | "order_id": 2, 144 | "status": "done", 145 | "size": 500, 146 | "user_id": "4", 147 | "reason": "cancelled", 148 | "timestamp": 1317086239.693123 149 | } 150 | }, 151 | { 152 | "type": "match", 153 | "timestamp": 1317086239.693123, 154 | "product_id": 0, 155 | "seq": 11, 156 | "payload": { 157 | "taker_id": 5, 158 | "provider_id": 0, 159 | "taker_user_id": "4", 160 | "provider_user_id": "2", 161 | "size": 100, 162 | "price": 1022000000, 163 | "provider_side": 0, 164 | "timestamp": 1317086239.693123 165 | } 166 | }, 167 | { 168 | "type": "order_status", 169 | "timestamp": 1317086239.693123, 170 | "product_id": 0, 171 | "seq": 12, 172 | "payload": { 173 | "order_id": 0, 174 | "status": "done", 175 | "size": 0, 176 | "user_id": "2", 177 | "reason": "filled", 178 | "timestamp": 1317086239.693123 179 | } 180 | }, 181 | { 182 | "type": "order_status", 183 | "timestamp": 1317086239.693123, 184 | "product_id": 0, 185 | "seq": 13, 186 | "payload": { 187 | "status": "open", 188 | "side": 1, 189 | "order_id": 5, 190 | "user_id": "4", 191 | "price": 1000000000, 192 | "size": 700, 193 | "timestamp": 1317086239.693123 194 | } 195 | } 196 | ] -------------------------------------------------------------------------------- /test/unit/trade_with_self/send.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "timestamp": 1311794529249, 4 | "type": "order", 5 | "payload": { 6 | "order_id": 0, 7 | "price": 1022000000, 8 | "user_id": "2", 9 | "side": 0, 10 | "size": 100 11 | } 12 | }, 13 | { 14 | "timestamp": 1311794529249, 15 | "type": "order", 16 | "payload": { 17 | "order_id": 1, 18 | "price": 1024000000, 19 | "user_id": "3", 20 | "side": 0, 21 | "size": 200 22 | } 23 | }, 24 | { 25 | "timestamp": 1311794529249, 26 | "type": "order", 27 | "payload": { 28 | "order_id": 2, 29 | "price": 1023000000, 30 | "user_id": "4", 31 | "side": 0, 32 | "size": 500 33 | } 34 | }, 35 | { 36 | "timestamp": 1311794529250, 37 | "type": "order", 38 | "payload": { 39 | "order_id": 5, 40 | "price": 1000000000, 41 | "user_id": "4", 42 | "side": 1, 43 | "size": 1000 44 | } 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /test/unit/trade_with_self/state.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "bids": [ 4 | { 5 | "price": 1022000000, 6 | "size": 100, 7 | "order_id": 0, 8 | "user_id": "2" 9 | } 10 | ], 11 | "asks": [], 12 | "state_num": 2, 13 | "output_seq": 3, 14 | "ticker": {}, 15 | "recovery_journal": "matcher_in.0.1317086239.log" 16 | }, 17 | { 18 | "bids": [ 19 | { 20 | "price": 1024000000, 21 | "size": 200, 22 | "order_id": 1, 23 | "user_id": "3" 24 | }, 25 | { 26 | "price": 1022000000, 27 | "size": 100, 28 | "order_id": 0, 29 | "user_id": "2" 30 | } 31 | ], 32 | "asks": [], 33 | "state_num": 2, 34 | "output_seq": 5, 35 | "ticker": {}, 36 | "recovery_journal": "matcher_in.0.1317086239.log" 37 | }, 38 | { 39 | "bids": [ 40 | { 41 | "price": 1024000000, 42 | "size": 200, 43 | "order_id": 1, 44 | "user_id": "3" 45 | }, 46 | { 47 | "price": 1023000000, 48 | "size": 500, 49 | "order_id": 2, 50 | "user_id": "4" 51 | }, 52 | { 53 | "price": 1022000000, 54 | "size": 100, 55 | "order_id": 0, 56 | "user_id": "2" 57 | } 58 | ], 59 | "asks": [], 60 | "state_num": 2, 61 | "output_seq": 7, 62 | "ticker": {}, 63 | "recovery_journal": "matcher_in.0.1317086239.log" 64 | }, 65 | { 66 | "bids": [], 67 | "asks": [ 68 | { 69 | "price": 1000000000, 70 | "size": 700, 71 | "order_id": 5, 72 | "user_id": "4" 73 | } 74 | ], 75 | "state_num": 2, 76 | "output_seq": 14, 77 | "ticker": { 78 | "price": 1022000000, 79 | "size": 100, 80 | "timestamp": 1317086239.693123 81 | }, 82 | "recovery_journal": "matcher_in.0.1317086239.log" 83 | } 84 | ] -------------------------------------------------------------------------------- /test/update_precision.py: -------------------------------------------------------------------------------- 1 | # a simple script to update the precision for the tests 2 | 3 | import json, sys, os 4 | 5 | def update(file): 6 | with open(file) as f: 7 | o = json.load(f); 8 | 9 | for x in o: 10 | p = x.get('payload') 11 | if not p: continue 12 | 13 | size = p.get('size') 14 | if not size: continue 15 | 16 | p['size'] = int(round(p['size'] * 1e8)) 17 | p['price'] = int(round(p['price'] * 1e8)) 18 | 19 | 20 | with open(file, 'w') as f: 21 | json.dump(o, f, indent=4) 22 | 23 | dirs = os.listdir('unit') 24 | for d in dirs: 25 | update(os.path.join('unit', d, "send.json")) 26 | --------------------------------------------------------------------------------