done
This commit is contained in:
parent
4803aa8518
commit
79d863b112
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM python:3
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY templates templates
|
||||
COPY main.py main.py
|
||||
|
||||
CMD [ "python", "main.py" ]
|
102
cache.csv
Normal file
102
cache.csv
Normal file
@ -0,0 +1,102 @@
|
||||
Date,1 Mo,2 Mo,3 Mo,4 Mo,6 Mo,1 Yr,2 Yr,3 Yr,5 Yr,7 Yr,10 Yr,20 Yr,30 Yr
|
||||
05/24/2024,5.56,5.53,5.46,5.51,5.44,5.21,4.93,4.71,4.53,4.49,4.46,4.65,4.57
|
||||
05/23/2024,5.51,5.48,5.46,5.51,5.44,5.2,4.91,4.71,4.52,4.5,4.47,4.67,4.58
|
||||
05/22/2024,5.49,5.48,5.45,5.5,5.43,5.16,4.86,4.64,4.47,4.44,4.43,4.63,4.55
|
||||
05/21/2024,5.5,5.47,5.45,5.51,5.42,5.14,4.82,4.61,4.43,4.42,4.41,4.65,4.55
|
||||
05/20/2024,5.5,5.47,5.45,5.51,5.43,5.15,4.82,4.62,4.46,4.44,4.44,4.68,4.58
|
||||
05/17/2024,5.5,5.47,5.46,5.5,5.41,5.14,4.83,4.6,4.44,4.43,4.42,4.66,4.56
|
||||
05/16/2024,5.51,5.47,5.45,5.53,5.41,5.13,4.78,4.58,4.4,4.39,4.38,4.62,4.52
|
||||
05/15/2024,5.49,5.46,5.45,5.5,5.4,5.1,4.73,4.51,4.35,4.35,4.36,4.61,4.52
|
||||
05/14/2024,5.5,5.47,5.44,5.51,5.43,5.16,4.81,4.62,4.46,4.45,4.45,4.69,4.59
|
||||
05/13/2024,5.5,5.47,5.45,5.51,5.43,5.16,4.85,4.66,4.5,4.49,4.48,4.72,4.63
|
||||
05/10/2024,5.51,5.47,5.47,5.51,5.43,5.17,4.87,4.65,4.52,4.51,4.5,4.74,4.64
|
||||
05/09/2024,5.51,5.48,5.46,5.5,5.41,5.12,4.8,4.6,4.47,4.46,4.45,4.7,4.6
|
||||
05/08/2024,5.51,5.47,5.45,5.5,5.41,5.13,4.84,4.63,4.5,4.49,4.48,4.73,4.64
|
||||
05/07/2024,5.51,5.48,5.45,5.51,5.41,5.13,4.82,4.6,4.48,4.47,4.47,4.7,4.61
|
||||
05/06/2024,5.51,5.48,5.45,5.5,5.42,5.12,4.82,4.64,4.48,4.48,4.49,4.73,4.64
|
||||
05/03/2024,5.51,5.48,5.45,5.5,5.41,5.12,4.81,4.63,4.48,4.49,4.5,4.75,4.66
|
||||
05/02/2024,5.51,5.47,5.46,5.5,5.42,5.16,4.87,4.71,4.57,4.57,4.58,4.82,4.72
|
||||
05/01/2024,5.47,5.5,5.46,5.51,5.43,5.21,4.96,4.79,4.64,4.64,4.63,4.85,4.74
|
||||
04/30/2024,5.48,5.51,5.46,5.45,5.44,5.25,5.04,4.87,4.72,4.71,4.69,4.9,4.79
|
||||
04/29/2024,5.48,5.51,5.45,5.45,5.43,5.2,4.97,4.8,4.65,4.64,4.63,4.86,4.75
|
||||
04/26/2024,5.48,5.51,5.46,5.45,5.4,5.21,4.96,4.84,4.68,4.68,4.67,4.89,4.78
|
||||
04/25/2024,5.48,5.51,5.47,5.46,5.41,5.21,4.96,4.85,4.7,4.71,4.7,4.93,4.82
|
||||
04/24/2024,5.49,5.5,5.46,5.44,5.4,5.17,4.89,4.78,4.64,4.66,4.65,4.88,4.78
|
||||
04/23/2024,5.49,5.5,5.45,5.44,5.39,5.14,4.86,4.76,4.63,4.62,4.61,4.84,4.73
|
||||
04/22/2024,5.49,5.5,5.42,5.44,5.39,5.16,4.97,4.81,4.66,4.65,4.62,4.84,4.72
|
||||
04/19/2024,5.49,5.51,5.45,5.44,5.39,5.17,4.97,4.81,4.66,4.65,4.62,4.83,4.72
|
||||
04/18/2024,5.49,5.52,5.46,5.44,5.39,5.18,4.98,4.83,4.68,4.67,4.64,4.85,4.74
|
||||
04/17/2024,5.49,5.49,5.45,5.44,5.38,5.16,4.93,4.77,4.62,4.61,4.59,4.81,4.71
|
||||
04/16/2024,5.49,5.51,5.45,5.44,5.39,5.18,4.97,4.83,4.69,4.69,4.67,4.88,4.77
|
||||
04/15/2024,5.49,5.51,5.45,5.44,5.38,5.16,4.93,4.78,4.65,4.65,4.63,4.85,4.74
|
||||
04/12/2024,5.48,5.5,5.45,5.42,5.36,5.13,4.88,4.7,4.54,4.53,4.5,4.73,4.61
|
||||
04/11/2024,5.48,5.51,5.45,5.44,5.38,5.17,4.93,4.77,4.61,4.6,4.56,4.77,4.65
|
||||
04/10/2024,5.49,5.5,5.45,5.44,5.4,5.19,4.97,4.77,4.61,4.59,4.55,4.76,4.64
|
||||
04/09/2024,5.48,5.49,5.43,5.41,5.34,5.03,4.74,4.52,4.37,4.38,4.36,4.6,4.5
|
||||
04/08/2024,5.48,5.49,5.43,5.41,5.35,5.07,4.78,4.6,4.43,4.43,4.42,4.65,4.55
|
||||
04/05/2024,5.47,5.5,5.43,5.41,5.34,5.05,4.73,4.54,4.38,4.39,4.39,4.65,4.54
|
||||
04/04/2024,5.47,5.49,5.41,5.4,5.32,5.0,4.65,4.46,4.3,4.31,4.31,4.57,4.47
|
||||
04/03/2024,5.47,5.44,5.42,5.4,5.33,5.03,4.68,4.48,4.34,4.36,4.36,4.61,4.51
|
||||
04/02/2024,5.49,5.45,5.42,5.4,5.34,5.05,4.7,4.51,4.35,4.37,4.36,4.61,4.51
|
||||
04/01/2024,5.49,5.47,5.44,5.41,5.36,5.06,4.72,4.51,4.34,4.33,4.33,4.58,4.47
|
||||
03/28/2024,5.49,5.48,5.46,5.42,5.38,5.03,4.59,4.4,4.21,4.2,4.2,4.45,4.34
|
||||
03/27/2024,5.5,5.47,5.45,5.41,5.36,4.99,4.54,4.36,4.18,4.18,4.2,4.45,4.36
|
||||
03/26/2024,5.5,5.47,5.46,5.41,5.36,5.0,4.56,4.38,4.22,4.23,4.24,4.49,4.4
|
||||
03/25/2024,5.51,5.48,5.46,5.41,5.36,5.0,4.54,4.39,4.23,4.25,4.25,4.51,4.42
|
||||
03/22/2024,5.51,5.47,5.46,5.4,5.34,4.98,4.59,4.36,4.2,4.22,4.22,4.47,4.39
|
||||
03/21/2024,5.51,5.48,5.48,5.4,5.36,5.01,4.62,4.42,4.26,4.28,4.27,4.53,4.44
|
||||
03/20/2024,5.5,5.47,5.47,5.41,5.36,5.01,4.59,4.41,4.25,4.28,4.27,4.53,4.45
|
||||
03/19/2024,5.52,5.48,5.48,5.41,5.39,5.06,4.68,4.47,4.31,4.31,4.3,4.54,4.44
|
||||
03/18/2024,5.52,5.48,5.48,5.41,5.39,5.06,4.73,4.52,4.36,4.35,4.34,4.57,4.46
|
||||
03/15/2024,5.52,5.48,5.48,5.41,5.38,5.05,4.72,4.51,4.33,4.33,4.31,4.55,4.43
|
||||
03/14/2024,5.52,5.48,5.48,5.42,5.38,5.04,4.68,4.46,4.29,4.3,4.29,4.54,4.44
|
||||
03/13/2024,5.52,5.47,5.48,5.41,5.37,5.01,4.61,4.37,4.19,4.2,4.19,4.45,4.35
|
||||
03/12/2024,5.52,5.48,5.48,5.41,5.37,5.0,4.58,4.33,4.15,4.16,4.16,4.42,4.31
|
||||
03/11/2024,5.5,5.47,5.48,5.41,5.35,4.95,4.51,4.26,4.08,4.09,4.1,4.36,4.26
|
||||
03/08/2024,5.51,5.48,5.46,5.4,5.34,4.92,4.48,4.25,4.06,4.08,4.09,4.36,4.26
|
||||
03/07/2024,5.51,5.48,5.47,5.4,5.34,4.93,4.5,4.28,4.07,4.09,4.09,4.35,4.25
|
||||
03/06/2024,5.5,5.47,5.47,5.4,5.35,4.95,4.55,4.32,4.12,4.12,4.11,4.36,4.24
|
||||
03/05/2024,5.5,5.47,5.47,5.4,5.35,4.94,4.54,4.32,4.13,4.15,4.13,4.39,4.27
|
||||
03/04/2024,5.51,5.49,5.48,5.42,5.37,4.98,4.61,4.39,4.21,4.23,4.22,4.48,4.36
|
||||
03/01/2024,5.54,5.49,5.42,5.41,5.27,4.94,4.54,4.32,4.17,4.2,4.19,4.46,4.33
|
||||
02/29/2024,5.53,5.5,5.45,5.43,5.3,5.01,4.64,4.43,4.26,4.28,4.25,4.51,4.38
|
||||
02/28/2024,5.5,5.51,5.45,5.43,5.31,5.0,4.64,4.44,4.26,4.28,4.27,4.53,4.4
|
||||
02/27/2024,5.5,5.52,5.45,5.47,5.33,5.03,4.7,4.5,4.32,4.34,4.31,4.57,4.44
|
||||
02/26/2024,5.5,5.52,5.47,5.47,5.34,5.03,4.69,4.48,4.29,4.32,4.28,4.53,4.4
|
||||
02/23/2024,5.49,5.51,5.46,5.46,5.32,5.0,4.67,4.45,4.28,4.28,4.26,4.51,4.37
|
||||
02/22/2024,5.49,5.51,5.45,5.45,5.32,5.02,4.69,4.49,4.33,4.35,4.33,4.58,4.47
|
||||
02/21/2024,5.5,5.5,5.44,5.45,5.32,4.98,4.64,4.43,4.3,4.33,4.32,4.59,4.49
|
||||
02/20/2024,5.49,5.49,5.44,5.45,5.32,4.97,4.59,4.38,4.25,4.28,4.27,4.56,4.44
|
||||
02/16/2024,5.48,5.51,5.44,5.45,5.31,4.98,4.64,4.43,4.29,4.31,4.3,4.58,4.45
|
||||
02/15/2024,5.49,5.51,5.43,5.45,5.3,4.93,4.56,4.36,4.22,4.25,4.24,4.54,4.42
|
||||
02/14/2024,5.48,5.51,5.43,5.45,5.31,4.94,4.56,4.38,4.25,4.27,4.27,4.57,4.45
|
||||
02/13/2024,5.48,5.52,5.45,5.46,5.32,4.99,4.64,4.44,4.31,4.33,4.31,4.59,4.46
|
||||
02/12/2024,5.49,5.51,5.43,5.43,5.27,4.87,4.46,4.25,4.13,4.16,4.17,4.48,4.37
|
||||
02/09/2024,5.49,5.51,5.44,5.43,5.26,4.86,4.48,4.25,4.14,4.17,4.17,4.48,4.37
|
||||
02/08/2024,5.49,5.51,5.44,5.42,5.24,4.83,4.46,4.22,4.12,4.15,4.15,4.47,4.36
|
||||
02/07/2024,5.47,5.49,5.43,5.4,5.23,4.83,4.41,4.16,4.06,4.09,4.09,4.41,4.31
|
||||
02/06/2024,5.48,5.5,5.44,5.41,5.23,4.82,4.39,4.14,4.03,4.07,4.09,4.39,4.29
|
||||
02/05/2024,5.49,5.5,5.42,5.42,5.25,4.87,4.46,4.27,4.13,4.16,4.17,4.46,4.35
|
||||
02/02/2024,5.49,5.51,5.43,5.42,5.22,4.81,4.36,4.14,3.99,4.02,4.03,4.33,4.22
|
||||
02/01/2024,5.49,5.51,5.42,5.38,5.15,4.68,4.2,3.96,3.8,3.83,3.87,4.21,4.1
|
||||
01/31/2024,5.53,5.46,5.42,5.4,5.18,4.73,4.27,4.05,3.91,3.95,3.99,4.34,4.22
|
||||
01/30/2024,5.53,5.47,5.42,5.38,5.19,4.8,4.36,4.14,4.0,4.03,4.06,4.4,4.28
|
||||
01/29/2024,5.53,5.46,5.42,5.37,5.19,4.76,4.29,4.1,3.97,4.02,4.08,4.42,4.31
|
||||
01/26/2024,5.54,5.45,5.44,5.39,5.19,4.78,4.34,4.15,4.04,4.1,4.15,4.49,4.38
|
||||
01/25/2024,5.54,5.48,5.44,5.39,5.19,4.76,4.28,4.12,4.01,4.07,4.14,4.49,4.38
|
||||
01/24/2024,5.52,5.44,5.44,5.4,5.22,4.83,4.34,4.19,4.06,4.14,4.18,4.52,4.41
|
||||
01/23/2024,5.53,5.46,5.45,5.38,5.21,4.81,4.31,4.16,4.06,4.11,4.14,4.48,4.38
|
||||
01/22/2024,5.53,5.47,5.46,5.39,5.22,4.83,4.37,4.14,4.03,4.07,4.11,4.44,4.32
|
||||
01/19/2024,5.54,5.47,5.45,5.39,5.21,4.84,4.39,4.18,4.08,4.12,4.15,4.47,4.36
|
||||
01/18/2024,5.53,5.48,5.45,5.39,5.2,4.8,4.34,4.13,4.04,4.1,4.14,4.48,4.37
|
||||
01/17/2024,5.54,5.47,5.47,5.4,5.2,4.8,4.34,4.12,4.02,4.07,4.1,4.42,4.31
|
||||
01/16/2024,5.54,5.47,5.45,5.37,5.18,4.7,4.22,4.02,3.95,4.01,4.07,4.43,4.3
|
||||
01/12/2024,5.55,5.47,5.45,5.37,5.16,4.65,4.14,3.92,3.84,3.91,3.96,4.32,4.2
|
||||
01/11/2024,5.54,5.47,5.46,5.38,5.22,4.75,4.26,4.02,3.9,3.95,3.98,4.32,4.18
|
||||
01/10/2024,5.53,5.46,5.46,5.39,5.23,4.82,4.37,4.1,3.99,4.01,4.04,4.35,4.2
|
||||
01/09/2024,5.53,5.46,5.47,5.38,5.24,4.82,4.36,4.09,3.97,4.0,4.02,4.33,4.18
|
||||
01/08/2024,5.54,5.48,5.49,5.39,5.24,4.82,4.36,4.11,3.97,3.99,4.01,4.33,4.17
|
||||
01/05/2024,5.54,5.48,5.47,5.41,5.24,4.84,4.4,4.17,4.02,4.04,4.05,4.37,4.21
|
||||
01/04/2024,5.56,5.48,5.48,5.41,5.25,4.85,4.38,4.14,3.97,3.99,3.99,4.3,4.13
|
||||
01/03/2024,5.54,5.54,5.48,5.41,5.25,4.81,4.33,4.07,3.9,3.92,3.91,4.21,4.05
|
||||
01/02/2024,5.55,5.54,5.46,5.41,5.24,4.8,4.33,4.09,3.93,3.95,3.95,4.25,4.08
|
|
248
main.py
Normal file
248
main.py
Normal file
@ -0,0 +1,248 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from enum import Enum
|
||||
|
||||
import pandas as pd
|
||||
import requests
|
||||
from flask import Flask, jsonify, render_template, request, redirect, make_response
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
TREASURY_URL = "https://home.treasury.gov/resource-center/data-chart-center/interest-rates/daily-treasury-rates.csv/2024/all?type=daily_treasury_yield_curve&field_tdr_date_value=2024&page&_format=csv"
|
||||
|
||||
CURRENT_DATE = 20240524 # Set to friday since market is closed
|
||||
|
||||
RATES = None
|
||||
|
||||
|
||||
class Side(str, Enum):
|
||||
BUY = "BUY"
|
||||
SELL = "SELL"
|
||||
|
||||
|
||||
# Since we know all the types of treasuries, just enumerate them
|
||||
class Instrument(str, Enum):
|
||||
I_1MO = "1 Mo"
|
||||
I_2MO = "2 Mo"
|
||||
I_3MO = "3 Mo"
|
||||
I_4MO = "4 Mo"
|
||||
I_6MO = "6 Mo"
|
||||
I_1YR = "1 Yr"
|
||||
I_2YR = "2 Yr"
|
||||
I_3YR = "3 Yr"
|
||||
I_5YR = "5 Yr"
|
||||
I_7YR = "7 Yr"
|
||||
I_10YR = "10 Yr"
|
||||
I_20YR = "12 Yr"
|
||||
I_30YR = "30 Yr"
|
||||
|
||||
@classmethod
|
||||
def from_text(cls, txt):
|
||||
if txt == "1 Mo":
|
||||
return Instrument.I_1MO
|
||||
elif txt == "2 Mo":
|
||||
return Instrument.I_2MO
|
||||
elif txt == "3 Mo":
|
||||
return Instrument.I_3MO
|
||||
elif txt == "4 Mo":
|
||||
return Instrument.I_4MO
|
||||
elif txt == "6 Mo":
|
||||
return Instrument.I_6MO
|
||||
elif txt == "1 Yr":
|
||||
return Instrument.I_1YR
|
||||
elif txt == "2 Yr":
|
||||
return Instrument.I_2YR
|
||||
elif txt == "3 Yr":
|
||||
return Instrument.I_3YR
|
||||
elif txt == "5 Yr":
|
||||
return Instrument.I_5YR
|
||||
elif txt == "7 Yr":
|
||||
return Instrument.I_7YR
|
||||
elif txt == "10 Yr":
|
||||
return Instrument.I_20YR
|
||||
elif txt == "12 Yr":
|
||||
return Instrument.I_20YR
|
||||
return Instrument.I_30YR
|
||||
|
||||
|
||||
class Transaction:
|
||||
def __init__(
|
||||
self, user: str, instrument: Instrument, quantity: int, date: int, side: Side, rate: float
|
||||
):
|
||||
self.user = user
|
||||
self.instrument: str = str(instrument.value)
|
||||
self.side: str = str(side.value)
|
||||
self.quantity = quantity
|
||||
self.date = date
|
||||
self.rate = rate
|
||||
|
||||
|
||||
class User:
|
||||
def __init__(self, balance: int = 100000) -> None:
|
||||
self.ID = str(uuid.uuid4())
|
||||
self.balance = balance
|
||||
|
||||
|
||||
USERS: dict[str, User] = {}
|
||||
TRANSACTIONS: dict[str, list[Transaction]] = {}
|
||||
|
||||
|
||||
def background_saver():
|
||||
while True:
|
||||
with open("users.p", "wb") as fp:
|
||||
pickle.dump(USERS, fp)
|
||||
with open("transactions.p", "wb") as fp:
|
||||
pickle.dump(TRANSACTIONS, fp)
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
def get_treasury_rates_for_year(year: int) -> pd.DataFrame | None:
|
||||
global RATES
|
||||
|
||||
use_cache = False
|
||||
if RATES is None:
|
||||
if os.path.isfile("./cache.csv"):
|
||||
if time.time() - os.path.getmtime("./cache.csv") < 60000:
|
||||
use_cache = True
|
||||
else:
|
||||
use_cache = True
|
||||
|
||||
if use_cache:
|
||||
csv: pd.DataFrame = pd.read_csv("./cache.csv")
|
||||
RATES = csv
|
||||
else:
|
||||
response = requests.get(TREASURY_URL)
|
||||
response.raise_for_status()
|
||||
raw_csv = response.content.decode("UTF-8")
|
||||
csv_io = io.StringIO(raw_csv)
|
||||
csv: pd.DataFrame = pd.read_csv(csv_io)
|
||||
csv.to_csv("./cache.csv", index=False)
|
||||
RATES = csv
|
||||
|
||||
ret = {}
|
||||
cols = list(RATES.columns.values)
|
||||
for col in cols:
|
||||
ret[col] = RATES[col].tolist()
|
||||
|
||||
dates = ret["Date"]
|
||||
del ret["Date"]
|
||||
return ret, dates
|
||||
|
||||
|
||||
@app.route("/api/transactions/<id>")
|
||||
def get_transactions_for_user(id):
|
||||
t = TRANSACTIONS.get(id)
|
||||
if t is None:
|
||||
return jsonify([])
|
||||
|
||||
return jsonify([{"term": tx.instrument, "quantity": tx.quantity, "side": tx.side, "date": tx.date, "rate": tx.rate} for tx in t])
|
||||
|
||||
|
||||
@app.route("/api/buy", methods=["POST"])
|
||||
def buy():
|
||||
content = request.json
|
||||
print(content)
|
||||
q = int(content["quantity"])
|
||||
i = content["instrument"]
|
||||
user_id = content["user_id"]
|
||||
cost = q * 100
|
||||
if USERS.get(user_id) is not None:
|
||||
if cost > USERS[user_id].balance:
|
||||
return "You do not have the funds for this transaction", 400
|
||||
else:
|
||||
return "User does not exist", 404
|
||||
|
||||
current_rate = RATES[i].tolist()[-1]
|
||||
|
||||
USERS[user_id].balance = USERS[user_id].balance - cost
|
||||
t = Transaction(user_id, Instrument.from_text(i), q, time.time(), Side.BUY, current_rate)
|
||||
|
||||
if TRANSACTIONS.get(user_id) is None:
|
||||
TRANSACTIONS[user_id] = [t]
|
||||
else:
|
||||
TRANSACTIONS[user_id].append(t)
|
||||
|
||||
return jsonify(t)
|
||||
|
||||
|
||||
@app.route("/api/login", methods=["POST"])
|
||||
def login():
|
||||
content = request.json
|
||||
id = content.get("id")
|
||||
if id is None:
|
||||
return "ID cannot be empty", 400
|
||||
u = USERS.get(id)
|
||||
if u is None:
|
||||
return "User does not exist", 404
|
||||
resp = make_response(redirect("/"))
|
||||
resp.set_cookie("userid", u.ID)
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/api/createuser", methods=["POST"])
|
||||
def createuser():
|
||||
u = User()
|
||||
USERS[u.ID] = u
|
||||
resp = make_response(redirect("/"))
|
||||
resp.set_cookie("userid", u.ID)
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/register")
|
||||
def register():
|
||||
if "userid" in request.cookies:
|
||||
return redirect("/")
|
||||
return render_template("register.html")
|
||||
|
||||
|
||||
@app.route("/logout")
|
||||
def logout():
|
||||
if "userid" not in request.cookies:
|
||||
return redirect("/register")
|
||||
resp = make_response(redirect("/register"))
|
||||
resp.set_cookie("userid", "", expires=0)
|
||||
return resp
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
if "userid" not in request.cookies:
|
||||
return redirect("/register")
|
||||
uid = request.cookies.get("userid")
|
||||
if uid not in USERS:
|
||||
resp = make_response(redirect("/register"))
|
||||
resp.set_cookie("userid", "", expires=0)
|
||||
return resp
|
||||
rates, dates = get_treasury_rates_for_year(2024)
|
||||
return render_template("index.html", rates=[rates], dates=dates, userid=uid, balance=USERS[uid].balance)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Load up files if they exist
|
||||
if os.path.isfile("./users.p"):
|
||||
print("Loading users")
|
||||
with open("./users.p", "rb") as fp:
|
||||
USERS = pickle.load(fp)
|
||||
|
||||
if os.path.isfile("./transactions.p"):
|
||||
print("Loading transactions")
|
||||
with open("./transactions.p", "rb") as fp:
|
||||
TRANSACTIONS = pickle.load(fp)
|
||||
|
||||
# Start persistence
|
||||
thread = threading.Thread(target=background_saver)
|
||||
thread.start()
|
||||
|
||||
# Start Server
|
||||
app.run(port=8008)
|
||||
|
||||
|
||||
thread = threading.Thread(target=background_saver)
|
||||
thread.start()
|
0
requirements.txt
Normal file
0
requirements.txt
Normal file
50
templates/base.html
Normal file
50
templates/base.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>DWSFi</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/js/tabler.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/css/tabler.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/css/tabler-flags.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/css/tabler-payments.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/css/tabler-vendors.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="page">
|
||||
<header class="navbar navbar-expand-sm navbar-light d-print-none">
|
||||
<div class="container-xl">
|
||||
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
|
||||
<a href="#">
|
||||
<img src="https://images.rawpixel.com/image_png_800/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvdjEwNDktMjIucG5n.png" width="110" height="32" alt="Tabler" class="navbar-brand-image" />
|
||||
</a>
|
||||
<span>DWSFi</span>
|
||||
</h1>
|
||||
|
||||
<div class="navbar-nav flex-row order-md-last">
|
||||
<div class="nav-item">
|
||||
<a href="/logout" class="nav-link d-flex lh-1 text-reset">
|
||||
<div class="d-xl-block ps-2">
|
||||
{% if userid is not none %}
|
||||
<div>{{userid}} - Logout</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="page-wrapper">
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
220
templates/index.html
Normal file
220
templates/index.html
Normal file
@ -0,0 +1,220 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="row row-deck row-cards">
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="subheader">Account Balance</div>
|
||||
<div class="ms-auto lh-1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-baseline">
|
||||
<div class="h1 mb-0 me-2">${{balance}}</div>
|
||||
<div class="me-auto">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header border-0">
|
||||
<div class="card-title">Treasury Yields</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div id="chart-yields" class="chart-lg"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Transactions</h3>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table card-table table-vcenter text-nowrap datatable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Term</th>
|
||||
<th>Quantity</th>
|
||||
<th>Date</th>
|
||||
<th>Side</th>
|
||||
<th>Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="txbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Buy/Sell</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<div class="form-label">Term</div>
|
||||
<select class="form-select" id="term">
|
||||
<option value="1 Mo">1 Mo</option>
|
||||
<option value="2 Mo">2 Mo</option>
|
||||
<option value="3 Mo">3 Mo</option>
|
||||
<option value="4 Mo">4 Mo</option>
|
||||
<option value="6 Mo">6 Mo</option>
|
||||
<option value="1 Yr">1 Yr</option>
|
||||
<option value="2 Yr">2 Yr</option>
|
||||
<option value="3 Yr">3 Yr</option>
|
||||
<option value="5 Yr">5 Yr</option>
|
||||
<option value="7 Yr">7 Yr</option>
|
||||
<option value="10 Yr">10 Yr</option>
|
||||
<option value="12 Yr">12 Yr</option>
|
||||
<option value="30 Yr">30 Yr</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Quantity</label>
|
||||
<input type="number" class="form-control" name="quantity" id="quantity" placeholder="100" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Side</label>
|
||||
<select class="form-select">
|
||||
<option value="BUY">Buy</option>
|
||||
<option value="SELL">Sell</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="buybtn" class="btn btn-primary ms-auto">Submit Order</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta17/dist/libs/apexcharts/dist/apexcharts.min.js" defer></script>
|
||||
<script>
|
||||
|
||||
document.getElementById("buybtn").addEventListener('click', () => {
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("POST", "/api/buy", true);
|
||||
r.onload = function () {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
r.setRequestHeader("Content-Type", "application/json")
|
||||
// Send data in below way from JS
|
||||
r.send(JSON.stringify({
|
||||
"quantity": document.getElementById('quantity').value,
|
||||
"instrument": document.getElementById("term").value,
|
||||
"user_id": "{{userid}}"
|
||||
}));
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
|
||||
window.ApexCharts && (new ApexCharts(document.getElementById('chart-yields'), {
|
||||
chart: {
|
||||
type: "line",
|
||||
fontFamily: 'inherit',
|
||||
parentHeightOffset: 0,
|
||||
height: 512,
|
||||
toolbar: {
|
||||
show: true,
|
||||
},
|
||||
animations: {
|
||||
enabled: false
|
||||
},
|
||||
},
|
||||
fill: {
|
||||
opacity: 1,
|
||||
},
|
||||
stroke: {
|
||||
width: 2,
|
||||
lineCap: "round",
|
||||
curve: "straight",
|
||||
},
|
||||
series: [
|
||||
{% for rate in rates %}
|
||||
{% for name, data in rate.items() %}
|
||||
{
|
||||
name: "{{name}}",
|
||||
data: {{data}}
|
||||
},
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
],
|
||||
tooltip: {
|
||||
theme: 'dark'
|
||||
},
|
||||
grid: {
|
||||
padding: {
|
||||
top: -20,
|
||||
right: 0,
|
||||
left: -4,
|
||||
bottom: -4
|
||||
},
|
||||
strokeDashArray: 4,
|
||||
},
|
||||
xaxis: {
|
||||
labels: {
|
||||
padding: 0,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
},
|
||||
type: 'datetime',
|
||||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
padding: 4
|
||||
},
|
||||
},
|
||||
labels: [
|
||||
{% for each in dates %}
|
||||
"{{each}}",
|
||||
{% endfor %}
|
||||
],
|
||||
|
||||
legend: {
|
||||
show: true,
|
||||
position: 'bottom',
|
||||
offsetY: 12,
|
||||
markers: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
radius: 100,
|
||||
},
|
||||
itemMargin: {
|
||||
horizontal: 8,
|
||||
vertical: 8
|
||||
},
|
||||
},
|
||||
})).render();
|
||||
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.withCredentials = true;
|
||||
xhr.open('GET', 'http://localhost:8008/api/transactions/{{userid}}');
|
||||
|
||||
xhr.onload = function() {
|
||||
console.log(xhr.response);
|
||||
var jsonResponse = JSON.parse(xhr.response);
|
||||
for (let i = 0; i < jsonResponse.length; i++) {
|
||||
var newRow="<tr><td>"+ jsonResponse[i].term + "</td><td>" + jsonResponse[i].quantity + "</td><td>" + jsonResponse[i].date + "</td><td>" + jsonResponse[i].side + "</td><td>"+ jsonResponse[i].rate +"</td></tr>";
|
||||
document.getElementById('txbody').innerHTML += newRow;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
64
templates/register.html
Normal file
64
templates/register.html
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
{% extends 'base.html' %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="row row-deck row-cards">
|
||||
<h1>Registration</h1>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4>Registration will assign you a UUID, please remember this UUID to login!</h4>
|
||||
|
||||
<button id="regbtn" class="btn btn-primary ms-auto">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-lg-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4>If you already have a UUID, enter it below to login</h4>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Enter ID</label>
|
||||
<input type="text" class="form-control" placeholder="User ID" id="uuidid" />
|
||||
</div>
|
||||
|
||||
<button id="loginbtn" class="btn btn-primary ms-auto">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("regbtn").addEventListener('click', () => {
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("POST", "/api/createuser", true);
|
||||
r.onreadystatechange = function () {
|
||||
r.onload = function () {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
r.setRequestHeader("Content-Type", "application/json")
|
||||
// Send data in below way from JS
|
||||
r.send();
|
||||
});
|
||||
|
||||
document.getElementById("loginbtn").addEventListener('click', () => {
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("POST", "/api/login", true);
|
||||
r.onreadystatechange = function () {
|
||||
r.onload = function () {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
r.setRequestHeader("Content-Type", "application/json")
|
||||
// Send data in below way from JS
|
||||
r.send(JSON.stringify({
|
||||
"id": document.getElementById('uuidid').value
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
BIN
transactions.p
Normal file
BIN
transactions.p
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user