|
@@ -13,8 +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, BANK_NAME
|
|
|
-from util import random_chars
|
|
|
+from game import CURRENCY_NAME, logger, DB_NAME, MIN_INTEREST_INTERVAL, BANK_NAME, MRO_INTERVAL, MRO_RUNNING_TIME, random_ownable_name
|
|
|
|
|
|
DBName = str
|
|
|
connections: Dict[DBName, db.Connection] = {}
|
|
@@ -349,8 +348,30 @@ def get_user_loans(user_id):
|
|
|
return current_cursor.fetchall()
|
|
|
|
|
|
|
|
|
-def bonds():
|
|
|
- execute('''
|
|
|
+def bonds(issuer_id=None, only_next_mro_qualified=False):
|
|
|
+ if issuer_id is not None:
|
|
|
+ issuer_condition = 'issuer.rowid = ?'
|
|
|
+ issuer_params = (issuer_id,)
|
|
|
+ else:
|
|
|
+ issuer_condition = '1'
|
|
|
+ issuer_params = ()
|
|
|
+ if only_next_mro_qualified:
|
|
|
+ only_next_mro_condition = ''' -- noinspection SqlResolve @ any/"bonds"
|
|
|
+ SELECT EXISTS(
|
|
|
+ SELECT *
|
|
|
+ FROM banks b
|
|
|
+ JOIN ownership o ON o.ownable_id = bonds.ownable_id
|
|
|
+ JOIN tender_calendar t ON t.maturity_dt = bonds.maturity_dt
|
|
|
+ WHERE bonds.issuer_id = b.user_id
|
|
|
+ AND bonds.coupon >= t.mro_interest
|
|
|
+ AND t.maturity_dt = (SELECT MIN(t2.maturity_dt) FROM tender_calendar t2 WHERE t2.maturity_dt > ?)
|
|
|
+ )
|
|
|
+ '''
|
|
|
+ only_next_mro_params = (current_db_timestamp(),)
|
|
|
+ else:
|
|
|
+ only_next_mro_condition = '1'
|
|
|
+ only_next_mro_params = ()
|
|
|
+ execute(f'''
|
|
|
SELECT
|
|
|
name,
|
|
|
coupon,
|
|
@@ -359,8 +380,10 @@ def bonds():
|
|
|
FROM bonds
|
|
|
JOIN ownables o on bonds.ownable_id = o.rowid
|
|
|
JOIN users issuer on bonds.issuer_id = issuer.rowid
|
|
|
+ WHERE ({issuer_condition})
|
|
|
+ AND ({only_next_mro_condition})
|
|
|
ORDER BY coupon * (maturity_dt - ?) DESC
|
|
|
- ''', (current_db_timestamp(),))
|
|
|
+ ''', (*issuer_params, *only_next_mro_params, current_db_timestamp(), ))
|
|
|
|
|
|
return current_cursor.fetchall()
|
|
|
|
|
@@ -477,10 +500,7 @@ def ownable_name_exists(name):
|
|
|
|
|
|
|
|
|
def new_stock(expiry, name=None):
|
|
|
- while name is None:
|
|
|
- name = random_chars(6)
|
|
|
- if ownable_name_exists(name):
|
|
|
- name = None
|
|
|
+ name = new_random_ownable_name(name)
|
|
|
|
|
|
execute('''
|
|
|
INSERT INTO ownables(name)
|
|
@@ -506,6 +526,14 @@ def new_stock(expiry, name=None):
|
|
|
return name
|
|
|
|
|
|
|
|
|
+def new_random_ownable_name(name):
|
|
|
+ while name is None:
|
|
|
+ name = random_ownable_name()
|
|
|
+ if ownable_name_exists(name):
|
|
|
+ name = None
|
|
|
+ return name
|
|
|
+
|
|
|
+
|
|
|
def ownable_id_by_name(ownable_name):
|
|
|
execute('''
|
|
|
SELECT rowid
|
|
@@ -537,16 +565,6 @@ def currency_id():
|
|
|
return current_cursor.fetchone()[0]
|
|
|
|
|
|
|
|
|
-def mro_id():
|
|
|
- execute('''
|
|
|
- SELECT rowid
|
|
|
- FROM ownables
|
|
|
- WHERE name = ?
|
|
|
- ''', (MRO_NAME,))
|
|
|
-
|
|
|
- return current_cursor.fetchone()[0]
|
|
|
-
|
|
|
-
|
|
|
def user_money(user_id):
|
|
|
execute('''
|
|
|
SELECT amount
|
|
@@ -897,6 +915,7 @@ def user_has_order_with_id(session_id, order_id):
|
|
|
|
|
|
def leaderboard():
|
|
|
score_expression = '''
|
|
|
+ -- noinspection SqlResolve @ any/"users"
|
|
|
SELECT (
|
|
|
SELECT COALESCE(SUM(
|
|
|
CASE -- sum score for each of the users ownables
|
|
@@ -1226,8 +1245,11 @@ 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]
|
|
|
+def pay_bond_interest(until=None):
|
|
|
+ if until is None:
|
|
|
+ current_dt = current_db_timestamp()
|
|
|
+ else:
|
|
|
+ current_dt = until
|
|
|
sec_per_year = 3600 * 24 * 365
|
|
|
interests = execute('''
|
|
|
SELECT
|
|
@@ -1266,13 +1288,60 @@ def pay_bond_interest():
|
|
|
|
|
|
# delete matured bonds
|
|
|
execute('''
|
|
|
+ DELETE FROM transactions
|
|
|
+ WHERE ownable_id IN (
|
|
|
+ SELECT ownable_id
|
|
|
+ FROM bonds
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ )
|
|
|
+ ''', (current_dt,))
|
|
|
+ execute('''
|
|
|
+ DELETE FROM orders
|
|
|
+ WHERE ownership_id IN (
|
|
|
+ SELECT o2.rowid
|
|
|
+ FROM bonds
|
|
|
+ JOIN ownables o on bonds.ownable_id = o.rowid
|
|
|
+ JOIN ownership o2 on o.rowid = o2.ownable_id
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ )
|
|
|
+ ''', (current_dt,))
|
|
|
+ execute('''
|
|
|
+ DELETE FROM order_history
|
|
|
+ WHERE ownership_id IN (
|
|
|
+ SELECT o2.rowid
|
|
|
+ FROM bonds
|
|
|
+ JOIN ownables o on bonds.ownable_id = o.rowid
|
|
|
+ JOIN ownership o2 on o.rowid = o2.ownable_id
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ )
|
|
|
+ ''', (current_dt,))
|
|
|
+ execute('''
|
|
|
+ DELETE FROM ownership
|
|
|
+ WHERE ownable_id IN (
|
|
|
+ SELECT ownable_id
|
|
|
+ FROM bonds
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ )
|
|
|
+ ''', (current_dt,))
|
|
|
+ execute('''
|
|
|
+ DELETE FROM ownables
|
|
|
+ WHERE rowid IN (
|
|
|
+ SELECT ownable_id
|
|
|
+ FROM bonds
|
|
|
+ WHERE ? > maturity_dt
|
|
|
+ )
|
|
|
+ ''', (current_dt,))
|
|
|
+ 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]
|
|
|
+def pay_loan_interest(until=None):
|
|
|
+ if until is None:
|
|
|
+ current_dt = current_db_timestamp()
|
|
|
+ else:
|
|
|
+ current_dt = until
|
|
|
sec_per_year = 3600 * 24 * 365
|
|
|
interests = execute('''
|
|
|
SELECT
|
|
@@ -1296,6 +1365,43 @@ def pay_loan_interest():
|
|
|
''', (current_dt, current_dt, MIN_INTEREST_INTERVAL,))
|
|
|
|
|
|
|
|
|
+def triggered_mros():
|
|
|
+ return execute('''
|
|
|
+ SELECT
|
|
|
+ rowid AS mro_id,
|
|
|
+ maturity_dt AS expiry,
|
|
|
+ mro_interest AS min_interest,
|
|
|
+ dt AS mro_dt
|
|
|
+ FROM tender_calendar
|
|
|
+ WHERE NOT executed
|
|
|
+ AND dt < ?
|
|
|
+ ''', (current_db_timestamp(),)).fetchall()
|
|
|
+
|
|
|
+
|
|
|
+def mro(mro_id, expiry, min_interest):
|
|
|
+ qualified_bonds = execute('''
|
|
|
+ SELECT bonds.ownable_id
|
|
|
+ FROM bonds
|
|
|
+ JOIN banks b ON bonds.issuer_id = b.user_id
|
|
|
+ JOIN ownership o ON o.ownable_id = bonds.ownable_id -- AND bonds.issuer_id = o.user_id
|
|
|
+ JOIN orders o2 ON o.rowid = o2.ownership_id AND NOT o2.buy
|
|
|
+ WHERE maturity_dt = ?
|
|
|
+ AND coupon >= ?
|
|
|
+ AND "limit" IS NULL or "limit" <= 1
|
|
|
+ ''', (expiry, min_interest)).fetchall()
|
|
|
+ for ownable_id, amount in qualified_bonds:
|
|
|
+ bank_order(buy=True,
|
|
|
+ ownable_id=ownable_id,
|
|
|
+ limit=1,
|
|
|
+ amount=amount,
|
|
|
+ expiry=expiry,
|
|
|
+ ioc=True)
|
|
|
+ execute('''
|
|
|
+ UPDATE tender_calendar
|
|
|
+ SET executed = TRUE
|
|
|
+ WHERE rowid = ?''', (mro_id,)) # TODO set mro to executed
|
|
|
+
|
|
|
+
|
|
|
def loan_recipient_id(loan_id):
|
|
|
execute('''
|
|
|
SELECT user_id
|
|
@@ -1365,3 +1471,21 @@ def issue_bond(user_id, ownable_name, coupon, maturity_dt):
|
|
|
INSERT INTO bonds(issuer_id, ownable_id, coupon, maturity_dt)
|
|
|
VALUES (?, (SELECT MAX(rowid) FROM ownables), ?, ?)
|
|
|
''', (user_id, coupon, maturity_dt))
|
|
|
+
|
|
|
+
|
|
|
+def update_tender_calendar():
|
|
|
+ last_mro_dt = execute('''
|
|
|
+ SELECT COALESCE((SELECT dt
|
|
|
+ FROM tender_calendar
|
|
|
+ ORDER BY dt DESC
|
|
|
+ LIMIT 1), ?)
|
|
|
+ ''', (current_db_timestamp(),)).fetchone()[0]
|
|
|
+
|
|
|
+ one_day = 24 * 3600
|
|
|
+ while last_mro_dt < current_db_timestamp() + one_day:
|
|
|
+ last_mro_dt += MRO_INTERVAL
|
|
|
+ maturity_dt = last_mro_dt + MRO_RUNNING_TIME
|
|
|
+ execute('''
|
|
|
+ INSERT INTO tender_calendar(dt, mro_interest, maturity_dt)
|
|
|
+ VALUES (?, ?, ?)
|
|
|
+ ''', (last_mro_dt, global_control_value('main_refinancing_operations'), maturity_dt))
|