This commit is contained in:
fawce
2012-01-12 17:05:25 -05:00
parent 10a9971e3d
commit b157230a01
6 changed files with 106 additions and 4 deletions
+14
View File
@@ -0,0 +1,14 @@
.bundle
db/*.sqlite3
log/*.log
*.log
tmp/**/*
tmp/*
doc/api
doc/app
*.swp
*~
.DS_Store
*.pyc
.tddium
.tddium-deploy-key
+6 -1
View File
@@ -52,10 +52,15 @@ A backtesting session is comprised of:
## Pre-requisites
You need to have the tornado and pymongo eggs installed:
easy_install tornado pymongo
easy_install tornado pymongo pyzmq
You need to have mongodb installed and running. Find your system at http://www.mongodb.org/downloads and set it up.
You need to have zeromq installed - http://www.zeromq.org/intro:get-the-software. If you are on mac, get homebrew https://github.com/mxcl/homebrew then:
brew install zeromq
### Database and Collections expected in MongoDB ###
QBT requires a running mongodb instance with a few collections:
+35
View File
@@ -0,0 +1,35 @@
"""
Small classes to assist with db access, timezone calculations, and so on.
"""
class DocWrap():
"""
Provides attribute access style on top of dictionary results from pymongo.
Allows you to access result['field'] as result.field.
Aliases result['_id'] to result.id.
"""
def __init__(self, store=None):
if(store == None):
self.store = {}
else:
self.store = store.copy()
if(self.store.has_key('_id')):
self.store['id'] = self.store['_id']
del(self.store['_id'])
def __setitem__(self,key,value):
if(key == '_id'):
self.store['id'] = value
else:
self.store[key] = value
def __getitem__(self, key):
if self.store.has_key(key):
return self.store[key]
def __getattr__(self,attrname):
if self.store.has_key(attrname):
return self.store[attrname]
else:
raise AttributeError("No attribute named {name}".format(name=attrname))
+36
View File
@@ -0,0 +1,36 @@
"""
qbt runs backtests using multiple processes and zeromq messaging.
Backtest is the primary process. It maintains both server and client sockets:
zmq sockets for internal processing:
====================================
- data sink, ZMQ.PULL. Port = port_start + 1
- backtest will connect to socket, and then spawn one process per datasource, passing the data sink url as a startup arg. Each
datasource process will bind to the socket, and start processing
- backtest is responsible for merging the data events from all sources into a serialized stream and relaying it to the
aggregators, merging agg results, and transmitting consolidated stream to event feed.
- agg source, ZMQ.PUSH. Port = port_start + 2
- agg sink, ZMQ.PULL. Port = port_start + 3
- control source, ZMQ.PUB. Port = port_start + 4
- all child processes must subscribe to this socket. Control commands:
- START -- begin processing
- KILL -- exit immediately
zmq sockets for backtest clients:
=================================
- orders sink, ZMQ.RESP. Port = port_start + 5
- backtest will connect (can you bind?) to this socket and await orders from the client. Order data will be processed against the streaming datafeed.
- event feed, ZMQ.RESP. Port = port_start + 6
- backtest will bind to this socket and respond to requests from client for more data. Response data will be the queue of events that
transpired since the last request.
"""
import multiprocessing
import zmq
class Backtest(object):
def __init__(self, port_start=10000):
40B8729046
+15 -3
View File
@@ -11,8 +11,8 @@ import uuid
import os
import logging
import datetime
import multiprocessing
logging.getLogger().setLevel(logging.DEBUG)
logger = logging.getLogger()
define("port", default=8888, help="run the qbt on the given port", type=int)
@@ -47,6 +47,7 @@ class Application(tornado.web.Application):
handlers = [
(r"/", MainHandler),
(r"/login", LoginHandler),
(r"/backtest", BacktestHandler)
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
@@ -81,8 +82,8 @@ class BaseHandler(tornado.web.RequestHandler):
class MainHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
self.write("Hello, world")
self.write("Hello, world. Try launching a <a href='/backtest'>backtest</a>.")
class LoginHandler(BaseHandler):
def get(self):
self.write('<html><body><form action="/login" method="post">'
@@ -113,6 +114,17 @@ class LoginHandler(BaseHandler):
logger.debug("setting user_id cookie to {id}".format(id=user_record['_id']))
self.set_secure_cookie(u"user_id", unicode(user_record['_id']))
class BacktestHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
self.write('<html><body><form action="/backtest" method="post">'
'<input type="submit" value="Launch">'
'</form></body></html>')
@tornado.web.authenticated
def post(self):
def main():
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(Application())
View File