|
@@ -13,7 +13,7 @@ from typing import Optional, Dict
|
|
|
from passlib.handlers.sha2_crypt import sha256_crypt
|
|
|
|
|
|
import db_setup
|
|
|
-from game import CURRENCY_NAME, logger, DB_NAME, MIN_INTEREST_INTERVAL, MRO_NAME
|
|
|
+from game import CURRENCY_NAME, logger, DB_NAME, MIN_INTEREST_INTERVAL, MRO_NAME, BANK_NAME
|
|
|
from util import random_chars
|
|
|
|
|
|
DBName = str
|
|
@@ -181,7 +181,7 @@ def send_ownable(from_user_id, to_user_id, ownable_id, amount):
|
|
|
raise AssertionError('Can not send negative amount')
|
|
|
|
|
|
bank_id_ = bank_id()
|
|
|
- if from_user_id != bank_id_:
|
|
|
+ if from_user_id != bank_id_ and not is_bond_of_user(ownable_id, from_user_id):
|
|
|
execute('''
|
|
|
UPDATE ownership
|
|
|
SET amount = amount - ?
|
|
@@ -191,7 +191,7 @@ def send_ownable(from_user_id, to_user_id, ownable_id, amount):
|
|
|
|
|
|
own(to_user_id, ownable_name_by_id(ownable_id))
|
|
|
|
|
|
- if to_user_id != bank_id_ or ownable_id != currency_id():
|
|
|
+ if to_user_id != bank_id_ and not is_bond_of_user(ownable_id, to_user_id):
|
|
|
execute('''
|
|
|
UPDATE ownership
|
|
|
SET amount = amount + ?
|
|
@@ -334,8 +334,8 @@ def bank_id():
|
|
|
execute('''
|
|
|
SELECT users.rowid
|
|
|
FROM users
|
|
|
- WHERE username = 'bank'
|
|
|
- ''')
|
|
|
+ WHERE username = ?
|
|
|
+ ''', (BANK_NAME,))
|
|
|
|
|
|
return current_cursor.fetchone()[0]
|
|
|
|
|
@@ -395,6 +395,22 @@ def get_user_loans(user_id):
|
|
|
return current_cursor.fetchall()
|
|
|
|
|
|
|
|
|
+def bonds():
|
|
|
+ execute('''
|
|
|
+ SELECT
|
|
|
+ name,
|
|
|
+ coupon,
|
|
|
+ datetime(maturity_dt, 'unixepoch', 'localtime'),
|
|
|
+ username
|
|
|
+ FROM bonds
|
|
|
+ JOIN ownables o on bonds.ownable_id = o.rowid
|
|
|
+ JOIN users issuer on bonds.issuer_id = issuer.rowid
|
|
|
+ ORDER BY coupon * (maturity_dt - ?) DESC
|
|
|
+ ''', (current_db_timestamp(),))
|
|
|
+
|
|
|
+ return current_cursor.fetchall()
|
|
|
+
|
|
|
+
|
|
|
def get_ownable_orders(user_id, ownable_id):
|
|
|
execute('''
|
|
|
SELECT
|
|
@@ -925,34 +941,45 @@ def user_has_order_with_id(session_id, order_id):
|
|
|
|
|
|
|
|
|
def leaderboard():
|
|
|
- execute('''
|
|
|
+ score_expression = '''
|
|
|
+ SELECT (
|
|
|
+ SELECT COALESCE(SUM(
|
|
|
+ CASE -- sum score for each of the users ownables
|
|
|
+ WHEN ownership.ownable_id = ? THEN ownership.amount
|
|
|
+ ELSE ownership.amount * (SELECT price
|
|
|
+ FROM transactions
|
|
|
+ WHERE ownable_id = ownership.ownable_id
|
|
|
+ ORDER BY rowid DESC -- equivalent to ordering by dt
|
|
|
+ LIMIT 1)
|
|
|
+ END
|
|
|
+ ), 0)
|
|
|
+ FROM ownership
|
|
|
+ WHERE ownership.user_id = users.rowid)
|
|
|
+ -
|
|
|
+ ( SELECT COALESCE(SUM(
|
|
|
+ amount
|
|
|
+ ), 0)
|
|
|
+ FROM loans
|
|
|
+ WHERE loans.user_id = users.rowid)
|
|
|
+ '''
|
|
|
+ execute(f'''
|
|
|
SELECT *
|
|
|
FROM ( -- one score for each user
|
|
|
SELECT
|
|
|
username,
|
|
|
- SUM(CASE -- sum score for each of the users ownables
|
|
|
- WHEN ownership.ownable_id = ? THEN ownership.amount
|
|
|
- ELSE ownership.amount * (SELECT price
|
|
|
- FROM transactions
|
|
|
- WHERE ownable_id = ownership.ownable_id
|
|
|
- ORDER BY rowid DESC -- equivalent to ordering by dt
|
|
|
- LIMIT 1)
|
|
|
- END
|
|
|
- ) score
|
|
|
- FROM users, ownership
|
|
|
- WHERE ownership.user_id = users.rowid
|
|
|
- AND users.username != 'bank'
|
|
|
- GROUP BY users.rowid
|
|
|
+ ({score_expression}) AS score
|
|
|
+ FROM users
|
|
|
+ WHERE users.username != ?
|
|
|
) AS scores
|
|
|
ORDER BY score DESC
|
|
|
LIMIT 50
|
|
|
- ''', (currency_id(),))
|
|
|
+ ''', (currency_id(), BANK_NAME))
|
|
|
|
|
|
return current_cursor.fetchall()
|
|
|
|
|
|
|
|
|
def user_wealth(user_id):
|
|
|
- execute('''
|
|
|
+ score_expression = '''
|
|
|
SELECT (
|
|
|
SELECT COALESCE(SUM(
|
|
|
CASE -- sum score for each of the users ownables
|
|
@@ -972,6 +999,9 @@ def user_wealth(user_id):
|
|
|
), 0)
|
|
|
FROM loans
|
|
|
WHERE loans.user_id = ?)
|
|
|
+ '''
|
|
|
+ execute(f'''
|
|
|
+ SELECT ({score_expression}) AS score
|
|
|
''', (currency_id(), user_id, user_id,))
|
|
|
|
|
|
return current_cursor.fetchone()[0]
|
|
@@ -1241,6 +1271,53 @@ def assign_banking_licence(user_id):
|
|
|
''', (user_id,))
|
|
|
|
|
|
|
|
|
+def pay_bond_interest():
|
|
|
+ current_dt = execute("SELECT CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)").fetchone()[0]
|
|
|
+ sec_per_year = 3600 * 24 * 365
|
|
|
+ interests = execute('''
|
|
|
+ SELECT
|
|
|
+ SUM(amount * (POWER(1 + coupon,
|
|
|
+ (MIN(CAST(? AS FLOAT), maturity_dt) - last_interest_pay_dt) / ?) - 1)
|
|
|
+ ) AS interest_since_last_pay,
|
|
|
+ o.user_id AS to_user_id,
|
|
|
+ bonds.issuer_id AS from_user_id
|
|
|
+ FROM bonds
|
|
|
+ JOIN ownership o on bonds.ownable_id = o.ownable_id
|
|
|
+ WHERE ? - last_interest_pay_dt > ? OR ? > maturity_dt -- every interval or when the bond expired
|
|
|
+ AND amount != 0
|
|
|
+ GROUP BY o.user_id, bonds.issuer_id
|
|
|
+ ''', (current_dt, sec_per_year, current_dt, MIN_INTEREST_INTERVAL, current_dt)).fetchall()
|
|
|
+
|
|
|
+ matured_bonds = execute('''
|
|
|
+ SELECT
|
|
|
+ amount,
|
|
|
+ o.user_id AS to_user_id,
|
|
|
+ bonds.issuer_id AS from_user_id
|
|
|
+ FROM bonds
|
|
|
+ JOIN ownership o on bonds.ownable_id = o.ownable_id
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ ''', (current_dt,)).fetchall()
|
|
|
+
|
|
|
+ # transfer the interest money
|
|
|
+ for amount, to_user_id, from_user_id in interests:
|
|
|
+ send_ownable(from_user_id, to_user_id, currency_id(), amount)
|
|
|
+
|
|
|
+ # pay back matured bonds
|
|
|
+ for amount, to_user_id, from_user_id in matured_bonds:
|
|
|
+ send_ownable(from_user_id, to_user_id, currency_id(), amount)
|
|
|
+
|
|
|
+ execute('''
|
|
|
+ UPDATE bonds
|
|
|
+ SET last_interest_pay_dt = ?
|
|
|
+ WHERE ? - last_interest_pay_dt > ?''', (current_dt, current_dt, MIN_INTEREST_INTERVAL,))
|
|
|
+
|
|
|
+ # delete matured bonds
|
|
|
+ execute('''
|
|
|
+ DELETE FROM bonds
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ ''', (current_dt,))
|
|
|
+
|
|
|
+
|
|
|
def pay_loan_interest():
|
|
|
current_dt = execute("SELECT CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)").fetchone()[0]
|
|
|
sec_per_year = 3600 * 24 * 365
|
|
@@ -1328,12 +1405,12 @@ def main_refinancing_operations():
|
|
|
... # TODO
|
|
|
|
|
|
|
|
|
-def issue_bond(user_id, ownable_name, coupon):
|
|
|
+def issue_bond(user_id, ownable_name, coupon, maturity_dt):
|
|
|
execute('''
|
|
|
INSERT INTO ownables(name)
|
|
|
VALUES (?)
|
|
|
''', (ownable_name,))
|
|
|
execute('''
|
|
|
- INSERT INTO bonds(issuer_id, ownable_id, coupon)
|
|
|
- VALUES (?, (SELECT MAX(rowid) FROM ownables), ?)
|
|
|
- ''', (user_id, coupon))
|
|
|
+ INSERT INTO bonds(issuer_id, ownable_id, coupon, maturity_dt)
|
|
|
+ VALUES (?, (SELECT MAX(rowid) FROM ownables), ?, ?)
|
|
|
+ ''', (user_id, coupon, maturity_dt))
|