#coding=utf-8 from flask import Flask, render_template, request, redirect, url_for, g, session, flash, jsonify from markupsafe import escape from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime, date, timedelta import os import json import time import random import string import numpy as np import pandas as pd import psycopg2 import plotly import plotly.express as px from portfolio_builder import MVO pd.options.plotting.backend = "plotly" # PARAMETERS CONFIGS = { "ENV": "development", "DEBUG": True, "SECRET_KEY": os.urandom(30), # Set the secret key for session authentication "PERMANENT_SESSION_LIFETIME": timedelta(minutes=60) } # SQL_CONFIG = dict( # database= os.getenv("PGDATABASE"), # user=os.getenv("PGUSER"), # host=os.getenv("PGHOST"), # port=os.getenv("PGPORT"), # password=os.getenv("PGPASSWORD") # ) SQL_CONFIG = dict( database="railway", user="postgres", host="containers-us-west-103.railway.app", port="5913", password="gv5Mh7cPjCm9YTjAmsYD" ) # SQL_CONFIG = { # 'database': "tpm", # 'user': "hsienchen", # 'host': "127.0.0.1", # 'port': "5432" # } app = Flask(__name__) app.config.from_mapping(CONFIGS) # Load Assets with open('assets_tw.json') as f: data_tw = json.load(f) with open('assets_us.json') as f: data_us = json.load(f) def login_required(): if not 'username' in session: return False else: return True def get_stock(conn, stock_list, tw): ## Query DB if tw==1: sql="SELECT ticker, date, price, return FROM stock_price_tw where ticker = ANY(%s);" with conn: with conn.cursor() as curs: curs.execute(sql, (stock_list, )) # print(curs.mogrify(sql, (stock_list,))) data= curs.fetchall() else: sql1="SELECT ticker, date, price, return FROM stock_price where ticker = ANY(%s)" sql2="SELECT ticker, date, price, return FROM stock_price_tw where ticker = ANY(%s) ;" with conn: with conn.cursor() as curs: curs.execute(sql1, (stock_list,)) data_us= curs.fetchall() curs.execute(sql2, (stock_list,)) data_tw= curs.fetchall() data = data_us+data_tw dfStock = pd.DataFrame(data, columns=['ticker', 'date', 'price', 'return']) dfStock['date'] = pd.to_datetime(dfStock['date']) dfStock = dfStock.drop_duplicates() g = dfStock.groupby('ticker') port = pd.concat([g.get_group(t).set_index('date')['price'] for t in stock_list], axis=1, join='inner') port.columns=stock_list return port # Define the route for the index pages @app.route('/') def index(): return render_template('base.html') # Login Page @app.route('/login') def login(): # for key in session: # print(key, session[key]) # print(session.get('username'), session['username'], session.get('username') and session['username']) return render_template('login.html') @app.route('/login', methods=['POST']) def login_post(): # Get the username and password from the form username = request.form.get('username') password = request.form.get('password') print(username, password) ## Connect to the database conn = psycopg2.connect(**SQL_CONFIG) with conn: with conn.cursor() as curs: curs.execute("select * from users where username = %s;", (username, )) data = curs.fetchone() conn.close() # Authentication if (data is None) or (username is None) or (password is None): flash('使用者代號不對或密碼不對,請再試一次。', 'danger') return render_template('login.html') elif check_password_hash(data[2], password): session['username'] = username session['user_id'] = data[0] session['privilege'] = data[-1] session['update_freq'] = 100 session['lastCreateTime'] = time.time() session['tw'] = 1 return redirect(url_for('index')) else: flash('使用者代號不對或密碼不對,請再試一次。', 'danger') return render_template('login.html') # Registration Page @app.route('/registration') def registration(): if login_required(): return redirect(url_for('index')) return render_template('registration.html') @app.route('/registration', methods=['POST']) def registration_post(): # Get the username and password from the form username = request.form.get('username') password = request.form.get('password') rep_password = request.form.get('rep-password') # check password if not password is None and password == rep_password: print(username, password) conn = psycopg2.connect(**SQL_CONFIG) ## Connect to the database with conn.cursor() as curs: curs.execute("select * from users where username = %s;", (username, )) data = curs.fetchone() if data is None: with conn: with conn.cursor() as curs: curs.execute("insert into users (username, password) values (%s, %s);", (username, generate_password_hash(password))) # conn.commit() else: flash('使用者已存在。', 'warning') return redirect(url_for('login')) conn.close() name = username.split('@')[0] flash(f'註冊成功! 歡迎您, {name}。', 'success') return redirect(url_for('login')) else: flash('密碼不符合,請再次輸入。', 'warning') return redirect(url_for('registration')) # Logout Page @app.route('/logout', methods=['GET']) def logout(): if login_required(): pass else: flash('請先登入。', 'warning') return redirect(url_for('login')) if 'username' in session: # for key in list(session.keys()): # print(key, session[key]) # for key in list(session.keys()): # session.pop(key) session.clear() return redirect(url_for('index')) @app.route('/strategy') def strategy(): if login_required(): pass else: flash('使用投組功能請先登入。', 'warning') return redirect(url_for('login')) session['tw'] = 0 return render_template('strategy_tw.html', data_us = data_us, data_tw=data_tw) @app.route('/strategy_tw') def strategy_tw(): if login_required(): pass else: flash('使用投組功能請先登入。', 'warning') return redirect(url_for('login')) session['tw'] = 1 return render_template('strategy_tw.html', data_tw=data_tw) @app.route('/postStock', methods=['POST']) def submit_stock_list(): if login_required(): pass else: print('NOT LOGIN!!') return redirect(url_for('index')) if not 'tw' in session: return redirect(url_for('index')) # Update Session print("-"*10, "UPDATE ASSET", "-"*10) if session['update_freq']==0: print('update to frquent!') return 'update to frquent!' else: session['update_freq']-=1 flash('Looks like you have changed your name!', 'warning') stock_list = request.form.get('stockList') # this is string stock_list = json.loads(stock_list) # Load stock_list as list session['currStockList'] = stock_list ## Query DB conn = psycopg2.connect(**SQL_CONFIG) port = get_stock(conn, stock_list, session['tw']) conn.close() fig = port.plot(title = 'Assets in portfolio', labels=dict(index="Date", value="Price", variable="Assets")) fig['layout'] = dict( autosize=True, legend={'title': {'text': 'Assets'}, 'tracegroupgap': 0}, title= {'text': 'Assets in portfolio'}, xaxis= {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'Date'}}, yaxis= {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'Price'}} ) fig.update_layout(legend=dict( yanchor="top", y=0.99, xanchor="left", x=0.01 )) print(type(stock_list)) # 序列化 graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder) for key in request.form: print(key, request.form[key]) # Do something with the stock list heres return graphJSON # return jsonify({'message': 'Stock list received successfully, NOOOOO'}) @app.route('/postPort', methods=['POST']) def buildPort(): # Login Required if login_required(): pass else: print('NOT LOGIN!!') return redirect(url_for('index')) if not 'tw' in session: return redirect(url_for('index')) # Stop frequently building strategy if time.time() - session['lastCreateTime'] < 20: print("UNTIL: ", time.time()-session['lastCreateTime']) return jsonify({'mes': '投資組合建立時間間隔(或與登入時間間隔)必須大於60秒!'}) session['lastCreateTime'] = time.time() print("-"*10) for key in request.form: print(key, request.form[key], type(request.form[key])) # Portfolio Info name = request.form.get('name') if name == '': prefix=''.join(random.choices(string.ascii_uppercase + string.digits, k=6)) name= prefix + f"-{round(time.time()%100, 2)}" comp = request.form.get('comp') ts = int(request.form.get('ts')) ts = datetime.fromtimestamp(ts/1000) role = request.form.get('role') ratio = float(request.form.get('ratio')) comment = request.form.get('comment') stock_list = json.loads(request.form.get('stockList')) # Algorithm MVO print("-"*10) print("Enter Algorithms") print("-"*10) # time.sleep(20) # Query DB market_asset = '0050.TW' if session['tw']==1 else 'SPY' conn = psycopg2.connect(**SQL_CONFIG) if 'market_asset' in stock_list: port = get_stock(conn, stock_list, session['tw']) market = port[market_asset] else: port = get_stock(conn, stock_list+[market_asset], session['tw']) market = port[market_asset] port = port[stock_list] length, num = port.shape tsize = int(length*ratio) # time label train_label = port.index[1:][:tsize] test_label = port.index[1:][tsize:] # data data_return = port.pct_change().dropna().to_numpy() market_return = market.pct_change().dropna().to_numpy() train = data_return[:tsize, :] test = data_return[tsize:, :] train_market = market_return[:tsize] test_market = market_return[tsize:] # optimization sol = MVO.opt(train, role=role) train_info = MVO.portfolio_info(sol, train, train_market) test_info = MVO.portfolio_info(sol, test, test_market) # print(sol, train_info, test_info) # print("-"*10) # print(ts, name, session.get('username'), comp, # role, test_info['annual_ret'], test_info['vol'], test_info['mdd'], test_info['annual_sr'], # test_info['beta'], test_info['alpha'], test_info['var10'], test_info['R2'], True, comment, stock_list, list(sol), sep=", ") # print("-"*10) data = (ts, name, session.get('username').split('@')[0], comp, role, ratio, test_info['annual_ret'], test_info['vol'], test_info['mdd'], test_info['annual_sr'], test_info['beta'], test_info['alpha'], test_info['var10'], test_info['R2'], True, comment, stock_list, list(sol)) sql='insert into strategy \ (date, name, username, competition, role, ratio, annual_ret, vol, mdd, annual_sr, beta, alpha, var10, R2, tw, comment, assets, assets_position)\ values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s);' with conn: with conn.cursor() as curs: curs.execute(sql, data) conn.close() print("\n------Write in Success--------\n") return '''投資組合已完成建立,請至 #TODO:insert link 查詢分析結果。''' @app.route('/custom') def custom(): if login_required(): pass else: flash('使用投組功能請先登入。', 'warning') return redirect(url_for('login')) return render_template('custom.html', message='No') @app.route('/result') def result(): if login_required(): pass else: flash('使用投組功能請先登入。', 'warning') return redirect(url_for('login')) sql="""select id, date, name, username, annual_ret, vol, annual_sr\ from strategy order by id desc limit 100;""" conn = psycopg2.connect(**SQL_CONFIG) with conn: with conn.cursor() as curs: curs.execute(sql) data= curs.fetchall() conn.close() return render_template('result.html', strategy_data=data) # @app.route('/result_tw') # def result_tw(): # if login_required(): # pass # else: # flash('使用投組功能請先登入。', 'warning') # return redirect(url_for('login')) # return render_template('result_tw.html') # handle login failed @app.errorhandler(401) def page_not_found(e): return Response('

Failed

') if __name__ == "__main__": app.run(host='0.0.0.0', port=8000)