You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

387 lines
13 KiB

from flask import Flask, render_template, request, redirect, url_for, g, session, flash, jsonify
# from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
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 = {
'database': "tpm",
'user': "hsienchen",
'host': "127.0.0.1",
'port': "5432"
}
app = Flask(__name__)
app.config.from_mapping(CONFIGS)
# Login Manager, Flask_Login Stuff
# login_manager = LoginManager()
# login_manager.init_app(app)
# login_manager.login_view = 'login'
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
# Load Assets
with open('assets_tw.json') as f:
data_tw = json.load(f)
with open('assets_us.json') as f:
data = json.load(f)
print(request.args.get('data'), 666)
return render_template('strategy_tw.html', stockOpts={**data, **data_tw})
@app.route('/strategy_tw')
def strategy_tw():
if login_required():
pass
else:
flash('使用投組功能請先登入。', 'warning')
return redirect(url_for('login'))
session['tw'] = 1
# Load Assets
with open('assets_tw.json') as f:
data = json.load(f)
print(request.args.get('data'), 666)
return render_template('strategy_tw.html', stockOpts=data)
@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'), 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 jsonify({'mes': '投資組合已完成建立,請至<a href"/">gooooooo</a>查詢分析結果。'})
@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'))
return render_template('result.html')
@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('<p>Failed</p>')
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8000)