def submit_order(): # Even before rendering the page, try to connect to RabbitMQ. If RabbitMQ # is not running, then redirect to an error page mq = ch = None try: mq: pika.BlockingConnection = get_mq() ch = mq.channel() ch.queue_declare(queue='incoming_order') except AMQPConnectionError as e: error_msg = "Webserver failed to connect to order queue" return redirect(url_for("exchange.error", error_msg=error_msg)) form = OrderSubmitForm(request.form) if request.method == "POST" and form.validate_on_submit(): new_order: Order = Order( security_symbol=form.security_symbol.data, side=form.side.data, size=form.size.data, price=form.price.data, all_or_none=form.all_or_none.data, immediate_or_cancel=form.immediate_or_cancel.data, owner_id=current_user.user_id) if new_order.side == "ask": user_existing_asset = get_db().query(Asset).get( (current_user.user_id, new_order.security_symbol)) if user_existing_asset is None or user_existing_asset.asset_amount < new_order.size: return redirect( url_for("exchange.error", error_msg="Insufficient assets")) else: logger.info( f"Subtracting {new_order.size} shares of {new_order.security_symbol} from {current_user}" ) user_existing_asset.asset_amount -= new_order.size if new_order.price is None: logger.info(f"Marking market order {new_order}") new_order.immediate_or_cancel = True db = get_db() db.add(new_order) db.commit() logger.info(f"{new_order} committed to database") ch.basic_publish(exchange='', routing_key='incoming_order', body=new_order.json) logger.info(f"{new_order} submitted to order queue") return redirect(url_for("exchange.dashboard")) return render_template("exchange/submit_order.html", form=form, title="Submit order")
def start_company(): form = StartCompanyForm(request.form) db = get_db() if request.method == "POST" and form.validate_on_submit(): new_company: Company = Company(symbol=form.company_symbol.data, name=form.company_name.data, initial_value=form.input_cash.data, initial_size=form.size.data, founder_id=current_user.user_id, market_price=form.input_cash.data / form.size.data) db.add(new_company) founder_cash = db.query(Asset).get((current_user.user_id, "_CASH")) founder_cash.asset_amount -= float(form.input_cash.data) # Since this is a new company, the founder will definitely not have # prior assets founder_stocks = Asset(owner_id=current_user.user_id, asset_symbol=form.company_symbol.data, asset_amount=form.size.data) db.add(founder_stocks) db.commit() logger.info(f"{new_company} committed to database") logger.info(f"{founder_stocks} committed to database") return redirect(url_for("exchange.dashboard")) return render_template("exchange/start_company.html", form=form, title="Start company")
def dashboard(): db = get_db() # Any amount of cash will be displayed cash = [a for a in current_user.assets if (a.asset_symbol == "_CASH")][0] cash.asset_amount_display = format_number(cash.asset_amount, locale="en_US") # Empty assets for stocks will not be displayed stocks = [ a for a in current_user.assets if (a.asset_symbol != "_CASH") and (a.asset_amount > 0) ] # Compute stocks' market value by company's market price * asset_amount # Then compute net worth by summing up cash and stocks market value net_worth = cash.asset_amount for stock in stocks: market_price = db.query(Company).get(stock.asset_symbol).market_price stock.market_value = market_price * stock.asset_amount stock.market_value_display = format_number(stock.market_value, locale="en_US") stock.asset_amount_display = format_number(int(stock.asset_amount), locale="en_US") net_worth += stock.market_value net_worth_display = format_number(net_worth, locale="en_US") return render_template("exchange/dashboard.html", cash=cash, stocks=stocks, net_worth=net_worth_display, title="Dashboard")
def register(): form = RegistrationForm(request.form) if request.method == "POST" and form.validate_on_submit(): db_session = get_db() if db_session.query(User).filter( User.username == form.username.data).first() is not None: form.username.errors.append("Username already taken!") else: new_user: User = User(username=form.username.data, password_hash=generate_password_hash( form.password.data)) db_session.add(new_user) db_session.commit() # Each user receives $10,000.00 of cash upon registration initial_cash_amount = 10000 initial_cash = Asset(owner_id=new_user.user_id, asset_symbol="_CASH", asset_amount=initial_cash_amount) db_session.add(initial_cash) db_session.commit() logger.info(f"{new_user} received ${initial_cash_amount:.2f}") return redirect(url_for("auth.login")) return render_template("auth/register.html", form=form)
def load_user(user_id): db_session = get_db() if user_id is not None: loaded_user = db_session.query(User).get(user_id) logger.info(f"Loading {loaded_user}") return loaded_user return None
def validate(self): supervalidate = Form.validate(self) db = get_db() if db.query(Company).get(self.company_symbol.data) is not None: self.company_symbol.errors.append("Company symbol already taken") return False return supervalidate
def view_company(company_symbol): db = get_db() company = db.query(Company).get(company_symbol) if company is None: return redirect( url_for("exchange.error", error_msg=f"Company {company_symbol} does not exist")) else: company.create_date_display = company.create_dttm.strftime("%Y-%m-%d") company.founder_name = company.founder.username return render_template("exchange/view_company.html", company=company, title=f"Company: {company_symbol}")
def login(): form = LoginForm(request.form) if request.method == "POST" and form.validate_on_submit(): db_session = get_db() user = db_session.query(User).filter( User.username == form.username.data).first() if (user is not None) and check_password_hash(user.password_hash, form.password.data): login_user(user) return redirect(url_for('exchange.dashboard')) else: form.password.errors.append( "Login failed; check your username and password") return render_template("auth/login.html", form=form)
def recent_orders(): """Render the most recent (up to) 50 orders """ db = get_db() ownership = (Order.owner_id == current_user.user_id) create_dttm_desc = Order.create_dttm.desc() recent_orders = db.query(Order).filter(ownership).order_by( create_dttm_desc).limit(50).all() for order in recent_orders: order.side_display = "Buy" if order.side == "bid" else "Sell" order.price_display = f"${order.price:.2f}" if order.price is not None else "any price available" order.create_dttm_display = order.create_dttm.strftime( "%Y-%m-%d %H:%M:%S") return render_template("exchange/recent_orders.html", orders=recent_orders, title="Recent orders")
def recent_transactions(): """Render the most recent (up to) 50 transactions """ db = get_db() order_ids = [o.order_id for o in current_user.orders] involves_current_user = Transaction.ask_id.in_(order_ids) \ | Transaction.bid_id.in_(order_ids) dttm_desc = Transaction.transact_dttm.desc() transactions = db.query(Transaction).filter( involves_current_user).order_by(dttm_desc).limit(50).all() for t in transactions: t.side_display = "Bought" if (t.bid_id in order_ids) else "Sold" t.dttm_display = t.transact_dttm.strftime("%Y-%m-%d %H:%M:%S") return render_template("exchange/recent_transactions.html", transactions=transactions, title="Recent transactions")
def stock_chart_data(): symbol = request.args['symbol'] zoom = request.args['zoom'] if "zoom" in request.args else "year" debug = int(request.args['debug']) if "debug" in request.args else 0 db = get_db() # Query all transactions with transaction dttm sorted from earlier to later tfilter = (Transaction.security_symbol == symbol) # Add the dttm filter, which depends on the zoom level cutoff = dt.datetime.utcnow() - ZOOM_CONFIGS[zoom].cutoff_offset scale_unit = ZOOM_CONFIGS[zoom].scale_unit tfilter = tfilter & (Transaction.transact_dttm >= cutoff) sort_key = Transaction.transact_dttm.asc() query = db.query(Transaction).filter(tfilter).order_by(sort_key) # Convert the set of transactions into a 2 column dataframe: price vs dttm df = pd.read_sql(query.statement, db.bind)[['transact_dttm', 'price']] if debug: # If debug is set to True, then return dummy data without reading # from database random.seed(10) df = pd.DataFrame({ "transact_dttm": [ dt.datetime.fromtimestamp( random.uniform(cutoff.timestamp(), dt.datetime.utcnow().timestamp())) for i in range(500) ], "price": [random.uniform(10, 100) for i in range(500)] }).sort_values('transact_dttm') agg_data_points = aggregate_stock_chart(df, zoom) agg_data_points_dict = [{ "t": dp.t * 1000, "o": dp.o, "h": dp.h, "l": dp.l, "c": dp.c, "y": dp.c } for dp in agg_data_points] max_price = 0 if pd.isna(df['price'].max()) else df['price'].max() min_price = 0 if pd.isna(df['price'].min()) else df['price'].min() price_std = 0 if pd.isna(df['price'].std()) else df['price'].std() data = { "labels": [], "datasets": [{ "label": "market price", "data": agg_data_points_dict }] } options = { "scales": { "x": { "type": 'time', "distribution": 'linear', "time": { "unit": scale_unit } }, "y": { "suggestedMax": max_price + 0.7 * price_std, "suggestedMin": max(0, min_price - 0.7 * price_std) } } } return jsonify({"data": data, "options": options})
def autocomplete_companies(): db = get_db() companies = db.query(Company).all() data = {c.symbol: None for c in companies} return jsonify(data)