From 12454f2c8c6939639cf377365396d5086287f554 Mon Sep 17 00:00:00 2001 From: fawce Date: Mon, 9 Jan 2012 15:29:39 -0500 Subject: [PATCH] added a simple data bootstrapper, to create a development environment --- README.md | 33 +++++++++++++++++++++++++++++---- qbt_data_bootstrap.py | 29 +++++++++++++++++++++++++++++ qbt_server.py | 37 ++++++++++++++++++++++++++----------- qbt_server.pyc | Bin 0 -> 5700 bytes 4 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 qbt_data_bootstrap.py create mode 100644 qbt_server.pyc diff --git a/README.md b/README.md index 45cc834c..9c529b8e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,25 @@ # qbt - Quantopian Backtesting Services +##Development Quickstart +Navigate to your mongodb installation and start your db server: + ./bin/mongodb + +Install the necessary python libraries: + easy_install tornado pymongo + +Create a development database with sample data, will create one qbt user: + python qbt_data_bootstrap.py --user_email=... --password=... + +To run the qbt against a local mongodb instance, navigate to the source dir and run + python qbt_server.py --mongodb_dbname=qbt + +To see all the available options: + python qbt_server.py --help +or + python qbt_data_bootstrap.py --help + + + qbt uses tornado to accept synchronous requests for backtesting sessions. The client of a backtesting session first invokes the _backtest_ endpoint: http://serverip/backtest?startdate=<>&enddate=<>... @@ -13,17 +33,22 @@ A backtesting session is comprised of: - an event stream delivered via zeromq ## Pre-requisites +You need to have the tornado and pymongo eggs installed: + easy_install tornado pymongo -### MongoDB Server ### +You need to have mongodb installed and running. Find your system at http://www.mongodb.org/downloads and set it up. + +### Database and Collections expected in MongoDB ### QBT requires a running mongodb instance with a few collections: -- user collection. See handlers.BaseHandler for code using this collection. Documents must have: +- user collection. See handlers.BaseHandler and handlers.LoginHandler for code using this collection. Documents must have: - email - standard email address - - encrypted_password - an sha2 hex digest of the password - - salt - a secret other than the password, which can be set in a secure cookie to maintain a session. if unset, will be set to sha2 hex of datetime.utcnow()--password + - salt - sha256 hex of: datetime.utcnow()--password + - encrypted_password - an sha256 hex digest of: salt--password - _id - standard issue mongodb primary key +## Authenticating ## Requesting a Backtest diff --git a/qbt_data_bootstrap.py b/qbt_data_bootstrap.py new file mode 100644 index 00000000..de4dd2d1 --- /dev/null +++ b/qbt_data_bootstrap.py @@ -0,0 +1,29 @@ +import tornado.auth +import tornado.httpserver +import tornado.ioloop +from tornado.options import define, options +import tornado.web +import pymongo +import bson +import hashlib +import base64 +import uuid +import os +import logging + +import qbt_server + +define("user_email", default="qbt@quantopian.com", help="email address for qbt user") +define("password", default="foobar", help="password for qbt user") + +def db_main(): + tornado.options.parse_command_line() + connection, db = qbt_server.connect_db() + + #create a user for testing + salt, encrypted_password = qbt_server.encrypt_password(None, options.password) + + db.users.insert({'email':options.user_email, 'encrypted_password':encrypted_password, 'salt':salt}) + +if __name__ == "__main__": + db_main() diff --git a/qbt_server.py b/qbt_server.py index ed8e39a9..cfd7f837 100644 --- a/qbt_server.py +++ b/qbt_server.py @@ -10,19 +10,38 @@ import base64 import uuid import os import logging +import datetime -logging.getLogger('qbt').setLevel(logging.DEBUG) -logger = logging.getLogger('qbt') +logging.getLogger().setLevel(logging.DEBUG) +logger = logging.getLogger() define("port", default=8888, help="run the qbt on the given port", type=int) define("mongodb_host", default="127.0.0.1", help="mongodb host address") define("mongodb_port", default=27017, help="connect to the mongodb on the given port", type=int) -define("mongodb_dbname", default="blog", help="database name") -define("mongodb_user", default="blog", help="database user") -define("mongodb_password", default="blog", help="database password") +define("mongodb_dbname", default="qbt", help="database name") +define("mongodb_user", default="qbt", help="database user") +define("mongodb_password", default="qbt", help="database password") HASH_ALGO = 'sha256' +def connect_db(): + connection = pymongo.Connection(options.mongodb_host, options.mongodb_port) + db = connection[options.mongodb_dbname] + db.authenticate(options.mongodb_user, options.mongodb_password) + return connection, db + +def encrypt_password(salt, password): + if(salt == None): + h1 = hashlib.new(HASH_ALGO) + h1.update(str(datetime.datetime.utcnow())+"--"+password) + salt = h1.hexdigest() + + h2 = hashlib.new(HASH_ALGO) + h2.update(salt+"--"+password) + encrypted_password = h2.hexdigest() + + return salt, encrypted_password + class Application(tornado.web.Application): def __init__(self): handlers = [ @@ -41,9 +60,7 @@ class Application(tornado.web.Application): tornado.web.Application.__init__(self, handlers, **settings) # Have one global connection to the blog DB across all handlers - self.connection = pymongo.Connection(options.mongodb_host, options.mongodb_port) - self.db = self.connection[options.mongodb_dbname] - self.db.authenticate(options.mongodb_user, options.mongodb_password) + self.connection, self.db = connect_db() class BaseHandler(tornado.web.RequestHandler): @@ -90,9 +107,7 @@ class LoginHandler(BaseHandler): #calculate password hash - h.update(user_record['salt']+"--"+password) - encrypted_password = h.hexdigest() - logger.debug("checking {password} as {encrypted_password} against {record}".format(password=password, encrypted_password=encrypted_password, record=user_record['encrypted_password'])) + salt, encrypted_password = encrypt_password(user_record['salt'], password) if (user_record['encrypted_password'] == encrypted_password): #we have a match, so set the secure cookie to the salt logger.debug("setting user_id cookie to {id}".format(id=user_record['_id'])) diff --git a/qbt_server.pyc b/qbt_server.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ce86ad1df71b00e8655488db18310b0fd437867 GIT binary patch literal 5700 zcmbVQTW{Og5gyXAZ26v>6K|X>qwN9)n%GU6%>uiQ(Zt=2w@93pPFhsG2$V=mOlVRo z9>p>{ z2`411NC=`T;gp2a5?#i)35R`<1$~eLeY4=Y%Ej*BVqb8vA7ltz~j&Uyy zxtGVdSCktrOXG@!SLGd4yDZ@~C9X=iB9SBUHHlYbA1eQdd>rhTjTH&6mwg{;-|DdM zx`a0*x~|-tL++}CAD3Kj$h{%qExt5vO7yXWb$R7VHVbXtL1N;+)G>iQ-6-khH$e0Usb{*8_if8~ znI${%n<({kE{B?i(bNpE}78-ml^o%7S64ceA@~z-Dm$6IYI* zn2MN50vCFTr-*KJ0ozxhk*f?490N58bxGWxSzw8GA#y`AQxr%#@%{RcfD%t9_VeZ#{mBL)s>j)7^X znVr~;-iC1}%FP-zqJEYKiP1SjOi`5XUA0B`p1Fv(C~fzTdL{~+<)9BU5dmGSFXy(l*D?(Db`) zj{O@N7y*6&B!{rFLb?MdP`3!-7HTWFLP5Y2U~!mReFZ`Pe-5b9Pmm9wJ_LAJCY}*U zAI}jO@%$N@s9~zW1Pofhh$^6F7MXpJdg6;j3(y5%{Rz%zkO4$~4KRu{FKdgKFYrZb zXBVe{U*xpzMv`^n)bPM2I_M>U0(5M;3Y3zQt5S0Vg|=-kr`mV*(YJYjzuC^R*D=7s zwJZ%cYxg6gYw14CK|g_KK&Me_&{M9IF6?AkNt z`U0s;oOU!>oI76m53%Svo}9`Px8^LmRp%D|s?MiQ)vdWJ6Lw>(2wD+ev&78)#Lv3yskO4K>k;m>JuHepEQKCIEPD2Y$(=;&eY#-hS3U z2#kLTJk`JT9wu5Hhhoj{-O7=ciNxovSCHZxD?mFNq()JyCR6_{j^;nZ;19B));p9O za`#avG`Z3ftWunS#oNQW-k8`~1i^~0S*F$2Z=SY16{+%>JwhT8MLM3=vbMuYx%r55@>2YY15 zbL){>`x%}h#`ugkLoHGxs>M%0SS?!RA%_59=m+YwB9EKddXX z8Hnp!EP&n{S=L15=;8a;nFaGf3tn6I-UP`2>+i*#)Qi*l*5(EU#D7-W4Hey5{3VX^ z9c{1H`vvA(!#hJA65Wc&AA+!WWUJ*ZVqihL43)(#0JSlqB;GAzxxlXG;GtT4`h-uD z+L*8iO3MIxbBI!v$toDB@cIXU)k= z2uiX~k!%*+OHQS-;#_cVyq|W|z5HKMB2FD`cJ3V+78zl+h_5=esamCt*Ryd>b)@En z6P}`zpjH$E0C*>~trz3gf~rO@E;>kS6~GLGfGSQ!oGq&`rW+gj8CCU)*9-bNi-v;( zRJhHA-)dOEb&cr(IJj!->&}Z8Ki$(;zs{M=CP|!|DD|1h^gYf{*z}bA=J`{S)98xv z!6!)DkpdTF--ZCSA2%s3^dm){!)h80-~%bNQeOJ!afe)TbW4=W=xa9DJc`CM7Z-qa z<~cpT9=+s_ot2JOvWxR*OtTk=_2LT_^Q!1)4?RXXfmaLliRbKA$M}rY=WI66Xt~6q z4Aq~%LIyAOh?TfCu~4JF;1o}zH<7(;{rIcLFQ2IYXiCR{t(WO_MV)CAQ>;lSx*vTz zz~!f?>iuo9ZnGii{MT$Ap&2P#sq<)@qHWq+$QH4abK^OOf6Llhx@cYS+*S1O!Jmuk UO|>?+dS!KSd2;z?ZED{64}AxC(f|Me literal 0 HcmV?d00001