mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-11 03:34:12 +08:00
aligned the authentication with quantopian.com standard
This commit is contained in:
@@ -1,3 +1,29 @@
|
||||
# Quantopian Backtesting Services
|
||||
# qbt - Quantopian Backtesting Services
|
||||
|
||||
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=<>...
|
||||
|
||||
qbt will respond with a json object describing the session:
|
||||
- backtest id, to be referenced in all further requests
|
||||
- zeromq connection information for the event stream
|
||||
|
||||
A backtesting session is comprised of:
|
||||
- REST endpoint to request orders
|
||||
- an event stream delivered via zeromq
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
### MongoDB Server ###
|
||||
QBT requires a running mongodb instance with a few collections:
|
||||
|
||||
- user collection. See handlers.BaseHandler 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
|
||||
- _id - standard issue mongodb primary key
|
||||
|
||||
|
||||
|
||||
|
||||
## Requesting a Backtest
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
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
|
||||
|
||||
logging.getLogger('qbt').setLevel(logging.DEBUG)
|
||||
logger = logging.getLogger('qbt')
|
||||
|
||||
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")
|
||||
|
||||
HASH_ALGO = 'sha256'
|
||||
|
||||
class Application(tornado.web.Application):
|
||||
def __init__(self):
|
||||
handlers = [
|
||||
(r"/", MainHandler),
|
||||
(r"/login", LoginHandler),
|
||||
]
|
||||
settings = dict(
|
||||
template_path=os.path.join(os.path.dirname(__file__), "templates"),
|
||||
static_path=os.path.join(os.path.dirname(__file__), "static"),
|
||||
xsrf_cookies=False,
|
||||
cookie_secret=base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),
|
||||
login_url="/login",
|
||||
#autoescape=None,
|
||||
debug=True,
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
class BaseHandler(tornado.web.RequestHandler):
|
||||
@property
|
||||
def db(self):
|
||||
return self.application.db
|
||||
|
||||
def get_current_user(self):
|
||||
user_id = self.get_secure_cookie(u"user_id")
|
||||
logger.info("looking up user with id: {id}".format(id=user_id))
|
||||
if not user_id: return None
|
||||
#get user record by id
|
||||
users = self.db.users.find(spec={"_id":bson.ObjectId(user_id)}, limit=1)
|
||||
if(users.count > 0):
|
||||
return users[0]
|
||||
return None
|
||||
|
||||
class MainHandler(BaseHandler):
|
||||
@tornado.web.authenticated
|
||||
def get(self):
|
||||
self.write("Hello, world")
|
||||
|
||||
class LoginHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.write('<html><body><form action="/login" method="post">'
|
||||
'Name: <input type="text" name="user_name">'
|
||||
'pass: <input type="password" name="password">'
|
||||
'<input type="submit" value="Sign in">'
|
||||
'</form></body></html>')
|
||||
|
||||
def post(self):
|
||||
self.authenticate(self.get_argument("user_name"),self.get_argument("password"))
|
||||
self.redirect("/")
|
||||
|
||||
def authenticate(self, username, password):
|
||||
h = hashlib.new(HASH_ALGO)
|
||||
#find user record by username.
|
||||
users = self.db.users.find(spec={"email":username}, limit=1)
|
||||
if(users.count > 0):
|
||||
user_record = users[0]
|
||||
else:
|
||||
logger.debug("no user with name: {username}", username=username)
|
||||
return
|
||||
|
||||
|
||||
#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']))
|
||||
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']))
|
||||
self.set_secure_cookie(u"user_id", unicode(user_record['_id']))
|
||||
|
||||
def main():
|
||||
tornado.options.parse_command_line()
|
||||
http_server = tornado.httpserver.HTTPServer(Application())
|
||||
http_server.listen(options.port)
|
||||
tornado.ioloop.IOLoop.instance().start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user