client_controller.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. import re
  2. import sys
  3. import time
  4. from datetime import datetime
  5. from getpass import getpass
  6. from inspect import signature
  7. import connection
  8. from connection import client_request
  9. from debug import debug
  10. from game import DEFAULT_ORDER_EXPIRY, CURRENCY_NAME, MIN_INTEREST_INTERVAL, CURRENCY_SYMBOL, OWNABLE_NAME_PATTERN
  11. from routes import client_commands
  12. from util import my_tabulate, yn_dialog
  13. exiting = False
  14. def login(username=None, password=None):
  15. if connection.session_id is not None:
  16. _fake_loading_bar('Signing out', duration=0.7)
  17. connection.session_id = None
  18. if username is None:
  19. username = input('Username: ')
  20. if password is None:
  21. if sys.stdin.isatty():
  22. password = getpass('Password: ')
  23. else:
  24. password = input('Password: ')
  25. _fake_loading_bar('Signing in', duration=2.3)
  26. response = client_request('login', {"username": username, "password": password})
  27. success = 'session_id' in response
  28. if success:
  29. connection.session_id = response['session_id']
  30. print('Login successful.')
  31. else:
  32. if 'error' in response:
  33. print('Login failed with message:', response['error'])
  34. else:
  35. print('Login failed.')
  36. def register(username=None, password=None, retype_pw=None):
  37. if connection.session_id is not None:
  38. connection.session_id = None
  39. _fake_loading_bar('Signing out', duration=0.7)
  40. if username is None:
  41. username = input('Username: ')
  42. if password is None:
  43. if sys.stdin.isatty():
  44. password = getpass('New password: ')
  45. retype_pw = getpass('Retype password: ')
  46. else:
  47. password = input('New password: ')
  48. retype_pw = input('Retype password: ')
  49. if password != retype_pw:
  50. print('Passwords do not match.')
  51. return
  52. elif retype_pw is None:
  53. if sys.stdin.isatty():
  54. retype_pw = getpass('Retype password: ')
  55. else:
  56. retype_pw = input('Retype password: ')
  57. if password != retype_pw:
  58. print('Passwords do not match.')
  59. return
  60. _fake_loading_bar('Validating Registration', duration=5.2)
  61. response = client_request('register', {"username": username, "password": password})
  62. if 'error' in response:
  63. print('Registration failed with message:', response['error'])
  64. def cancel_order(order_no=None):
  65. if order_no is None:
  66. order_no = input('Order No.: ')
  67. _fake_loading_bar('Validating Request', duration=0.6)
  68. response = client_request('cancel_order', {"session_id": connection.session_id, "order_id": order_no})
  69. if 'error' in response:
  70. print('Order cancelling failed with message:', response['error'])
  71. def change_pw(password=None, retype_pw=None):
  72. if password != retype_pw:
  73. password = None
  74. if password is None:
  75. if sys.stdin.isatty():
  76. password = getpass('New password: ')
  77. retype_pw = getpass('Retype password: ')
  78. else:
  79. password = input('New password: ')
  80. retype_pw = input('Retype password: ')
  81. if password != retype_pw:
  82. print('Passwords do not match.')
  83. return
  84. elif retype_pw is None:
  85. if sys.stdin.isatty():
  86. retype_pw = getpass('Retype password: ')
  87. else:
  88. retype_pw = input('Retype password: ')
  89. if password != retype_pw:
  90. print('Passwords do not match.')
  91. return
  92. _fake_loading_bar('Validating password', duration=1.2)
  93. _fake_loading_bar('Changing password', duration=5.2)
  94. response = client_request('change_password', {"session_id": connection.session_id, "password": password})
  95. if 'error' in response:
  96. print('Changing password failed with message:', response['error'])
  97. _fake_loading_bar('Signing out', duration=0.7)
  98. connection.session_id = None
  99. # noinspection PyShadowingBuiltins
  100. def help(command=None):
  101. print('Allowed commands:')
  102. command_table = []
  103. if command is None:
  104. for cmd in client_commands:
  105. this_module = sys.modules[__name__]
  106. method = getattr(this_module, cmd)
  107. params = signature(method).parameters
  108. command_table.append([cmd] + [p for p in params])
  109. print(my_tabulate(command_table, tablefmt='pipe', headers=['command',
  110. 'param 1',
  111. 'param 2',
  112. 'param 3',
  113. 'param 4',
  114. 'param 5',
  115. 'param 6',
  116. ]))
  117. print('NOTE:')
  118. print(' All parameters for all commands are optional!')
  119. print(' Commands can be combined in one line with ; between them.')
  120. print(' Parameters are separated with whitespace.')
  121. print(' Use . as a decimal separator.')
  122. print(' Use 0/1 for boolean parameters (other strings might work).')
  123. else:
  124. for cmd in client_commands:
  125. if cmd.strip() == command.strip():
  126. this_module = sys.modules[__name__]
  127. method = getattr(this_module, cmd)
  128. params = signature(method).parameters
  129. command_table.append([cmd] + [p for p in params])
  130. break
  131. else:
  132. print('Command not found:', command)
  133. return
  134. print(my_tabulate(command_table, tablefmt='pipe', headers=['command',
  135. *[f'param {idx}' for idx in range(len(params))],
  136. ]))
  137. def depot():
  138. _fake_loading_bar('Loading data', duration=1.3)
  139. response = client_request('depot', {"session_id": connection.session_id})
  140. success = 'data' in response and 'own_wealth' in response
  141. if success:
  142. data = response['data']
  143. for row in data:
  144. row.append(row[1] * row[2])
  145. print(my_tabulate(data,
  146. headers=['Object', 'Avail. Amount', 'Course', 'Bid', 'Ask', 'Est. Value'],
  147. tablefmt="pipe",
  148. floatfmt='.2f'))
  149. wealth = response['own_wealth']
  150. print(f'Taking into account the amount of debt you have, this results in a wealth of roughly {wealth}.')
  151. if response['banking_license']:
  152. print(f'Also, you have a banking license.')
  153. minimum_reserve = response['minimum_reserve']
  154. print(f'You are legally obligated to deposit a minimum cash reserve of {minimum_reserve}{CURRENCY_SYMBOL} '
  155. f'at the central bank as a security for your credits.')
  156. print('This minimum reserve has already been subtracted from the displayed amount.')
  157. else:
  158. if 'error' in response:
  159. print('Depot access failed with message:', response['error'])
  160. else:
  161. print('Depot access failed.')
  162. def leaderboard():
  163. _fake_loading_bar('Loading data', duration=1.3)
  164. response = client_request('leaderboard', {})
  165. success = 'data' in response
  166. if success:
  167. print(my_tabulate(response['data'], headers=['Trader', 'Wealth'], tablefmt="pipe"))
  168. # print('Remember that the goal is to be as rich as possible, not to be richer than other traders!')
  169. else:
  170. if 'error' in response:
  171. print('Leaderboard access failed with message:', response['error'])
  172. else:
  173. print('Leaderboard access failed.')
  174. def _order(is_buy_order, obj_name=None, amount=None, limit=None, stop_loss=None, expiry=None, ioc=None):
  175. if obj_name is None: # TODO list some available objects
  176. obj_name = input('Name of object to sell: ')
  177. obj_name = obj_name.strip()
  178. if obj_name == '':
  179. return
  180. if stop_loss is not None and stop_loss.strip() not in ['1', '0']:
  181. print('Invalid value for flag stop loss (only 0 or 1 allowed).')
  182. return
  183. elif stop_loss is not None:
  184. stop_loss = bool(int(stop_loss))
  185. if obj_name == CURRENCY_NAME.replace(CURRENCY_SYMBOL, 'K'):
  186. obj_name = CURRENCY_NAME
  187. if amount is None:
  188. amount = input('Amount: ')
  189. if amount == '':
  190. return
  191. if limit is None:
  192. set_limit = yn_dialog('Do you want to place a limit?')
  193. if set_limit:
  194. limit = input('Limit: ')
  195. stop_loss = yn_dialog('Is this a stop-loss limit?')
  196. if limit is not None:
  197. try:
  198. limit = float(limit)
  199. except ValueError:
  200. print('Invalid limit.')
  201. return
  202. if stop_loss is None:
  203. stop_loss = yn_dialog('Is this a stop-loss limit?')
  204. question = 'Are you sure you want to use such a low limit (limit=' + str(limit) + ')?:'
  205. if not is_buy_order and limit <= 0 and not yn_dialog(question):
  206. print('Order was not placed.')
  207. return
  208. if expiry is None:
  209. expiry = input('Time until order expires (minutes, default ' + str(DEFAULT_ORDER_EXPIRY) + '):')
  210. if expiry == '':
  211. expiry = DEFAULT_ORDER_EXPIRY
  212. try:
  213. expiry = float(expiry)
  214. except ValueError:
  215. print('Invalid expiration time.')
  216. return
  217. if ioc is None and not stop_loss:
  218. ioc = yn_dialog('Is this an IOC (immediate-or-cancel) order?')
  219. if str(ioc).strip() not in ['1', '0', 'True', 'False']:
  220. print('Invalid value for flag IOC (only 0 or 1 allowed).')
  221. return
  222. else:
  223. ioc = bool(int(ioc))
  224. _fake_loading_bar('Sending Data', duration=1.3)
  225. response = client_request('order', {
  226. "buy": is_buy_order,
  227. "session_id": connection.session_id,
  228. "amount": amount,
  229. "ownable": obj_name,
  230. "limit": limit,
  231. "stop_loss": stop_loss,
  232. "time_until_expiration": expiry,
  233. "ioc": ioc,
  234. })
  235. if 'error' in response:
  236. print('Order placement failed with message:', response['error'])
  237. else:
  238. print('You might want to use the `trades` or `depot` commands',
  239. 'to see if the order has been executed already.')
  240. def sell(obj_name=None, amount=None, limit=None, stop_loss=None, expiry=None, ioc=None):
  241. _order(is_buy_order=False,
  242. obj_name=obj_name,
  243. amount=amount,
  244. limit=limit,
  245. stop_loss=stop_loss,
  246. expiry=expiry,
  247. ioc=ioc, )
  248. def buy(obj_name=None, amount=None, limit=None, stop_loss=None, expiry=None, ioc=None):
  249. _order(is_buy_order=True,
  250. obj_name=obj_name,
  251. amount=amount,
  252. limit=limit,
  253. stop_loss=stop_loss,
  254. expiry=expiry,
  255. ioc=ioc, )
  256. def orders():
  257. _fake_loading_bar('Loading Data', duration=0.9)
  258. response = client_request('orders', {"session_id": connection.session_id})
  259. success = 'data' in response
  260. if success:
  261. print(my_tabulate(response['data'],
  262. headers=['Buy?', 'Name', 'Size', 'Limit', 'stop-loss', 'Expires', 'No.'],
  263. tablefmt="pipe"))
  264. else:
  265. if 'error' in response:
  266. print('Order access failed with message:', response['error'])
  267. else:
  268. print('Order access failed.')
  269. def buy_banking_license():
  270. _fake_loading_bar('Loading data', duration=2.3)
  271. _fake_loading_bar('Hiring some lawyers and consultants', duration=4.6)
  272. _fake_loading_bar('Overcoming some bureaucratic hurdles', duration=8.95)
  273. _fake_loading_bar('Filling the necessary forms', duration=14.4)
  274. _fake_loading_bar('Waiting for bank regulation\'s response', duration=26.8)
  275. response = client_request('buy_banking_license', {"session_id": connection.session_id})
  276. success = 'message' in response and 'error' not in response
  277. if success:
  278. print('Success. You are now a bank.')
  279. print()
  280. summarize_bank_rules()
  281. # print('Remember that the goal is to be as rich as possible, not to be richer than other traders!')
  282. else:
  283. if 'error' in response:
  284. print('Banking license application failed with message:', response['error'])
  285. else:
  286. print('Banking license application access failed.')
  287. def summarize_bank_rules():
  288. variables = _global_variables()
  289. banking_license_price = variables['banking_license_price']
  290. main_refinancing_operations = variables['main_refinancing_operations']
  291. cash_reserve_free_amount = variables['cash_reserve_free_amount']
  292. cash_reserve_ratio = variables['cash_reserve_ratio']
  293. deposit_facility = variables['deposit_facility']
  294. print(f'A bank is by definition anyone who has a banking license.')
  295. print(f'A banking license can be for {banking_license_price} {CURRENCY_NAME}.')
  296. print(f'This includes payment of lawyers and consultants to deal with the formal application.')
  297. print()
  298. print(f'Banks are allowed to borrow money from the central bank at an interest rate specified by the central bank'
  299. f' (currently {main_refinancing_operations * 100}% p.a.).')
  300. print(f'For every {CURRENCY_NAME} above {cash_reserve_free_amount} banks have to '
  301. f'deposit a cash reserve of {cash_reserve_ratio * 100}% at the central bank.')
  302. receive_or_pay = 'receive' if deposit_facility >= 0 else 'receive (or pay)'
  303. print(f'Banks {receive_or_pay} a deposit facility rate of {deposit_facility * 100}% p.a. for cash reserves at the central bank '
  304. f'(for banks, any money in the depot is deposited at the central bank).')
  305. print()
  306. print(f'NOTE: The sign of interest rates matters.')
  307. print(f'If an interest rate is negative, this actually means that the borrower get interest for borrowing money.')
  308. def summarize_loan_rules():
  309. variables = _global_variables()
  310. personal_loan_interest_rate = variables['personal_loan_interest_rate']
  311. print(f'You can take personal loans at an interest rate of {personal_loan_interest_rate * 100}% p.a.')
  312. print(f'You can repay personal loans at any time if you have the liquidity.')
  313. print(f'Interest rates will be automatically be debited from your account.')
  314. print(f'Note that this debit may be delayed by up to {MIN_INTEREST_INTERVAL} seconds.')
  315. print(f'If you have no {CURRENCY_NAME} available (or negative account balance), you can still')
  316. print(f' - pay any further interests (account will move further into the negative)')
  317. print(f' - take a new loan (if we think that you will be able to repay it)')
  318. print(f' - sell any securities you own')
  319. print(f'However you can not')
  320. print(f' - spend money to buy securities')
  321. print(f' - spend money to buy a banking license')
  322. def take_out_personal_loan(amount=None):
  323. if amount is None:
  324. summarize_loan_rules()
  325. print()
  326. amount = input('Please enter your desired loan volume: ')
  327. try:
  328. amount = float(amount)
  329. except ValueError:
  330. print('Amount must be a number larger than 0.')
  331. return
  332. if amount <= 0:
  333. print('Amount must be a number larger than 0.')
  334. _fake_loading_bar('Checking if you are trustworthy', duration=0.0)
  335. _fake_loading_bar('Checking if you are credit-worthy', duration=0.0)
  336. _fake_loading_bar('Transferring the money', duration=1.6)
  337. response = client_request('take_out_personal_loan', {"session_id": connection.session_id, 'amount': amount})
  338. success = 'message' in response and 'error' not in response
  339. if success:
  340. print(f'You took out a personal loan of {amount} {CURRENCY_NAME}.')
  341. else:
  342. if 'error' in response:
  343. print('Taking out a personal loan failed with message:', response['error'])
  344. else:
  345. print('Taking out a personal loan failed.')
  346. def loans():
  347. _fake_loading_bar('Loading Data', duration=0.9)
  348. response = client_request('loans', {"session_id": connection.session_id})
  349. success = 'data' in response
  350. if success:
  351. for row in response['data']:
  352. row[-1] = f'{row[-1] * 100:.2f}%'
  353. print(my_tabulate(response['data'],
  354. headers=['Loan ID', 'Total amount', 'Remaining', 'Interest p.a.', ],
  355. floatfmt='.2f',
  356. tablefmt="pipe"))
  357. else:
  358. if 'error' in response:
  359. print('Order access failed with message:', response['error'])
  360. else:
  361. print('Order access failed.')
  362. def credits():
  363. _fake_loading_bar('Loading Data', duration=1.6)
  364. response = client_request('credits', {"session_id": connection.session_id})
  365. success = 'data' in response
  366. if success:
  367. _print_credits_table(response)
  368. else:
  369. if 'error' in response:
  370. print('Listing credits failed with message:', response['error'])
  371. else:
  372. print('Listing credits failed.')
  373. def mro_qualified_credits():
  374. print('This command lists credits that you can sell to the central bank during the next mean refinancing operations.')
  375. print('The schedule is called tender calendar and visible through by command `tender_calendar`.')
  376. _fake_loading_bar('Loading Data', duration=1.6)
  377. response = client_request('credits', {"session_id": connection.session_id, 'only_next_mro_qualified': True})
  378. success = 'data' in response
  379. if success:
  380. _print_credits_table(response)
  381. else:
  382. if 'error' in response:
  383. print('Listing credits failed with message:', response['error'])
  384. else:
  385. print('Listing credits failed.')
  386. def _print_credits_table(response):
  387. for row in response['data']:
  388. row[1] = f'{row[1] * 100:.4f}%'
  389. print(my_tabulate(response['data'],
  390. headers=['Bond', 'Coupon', 'Maturity', 'Issuer', ],
  391. floatfmt='.2f',
  392. tablefmt="pipe"))
  393. def repay_loan(loan_id=None, amount=None):
  394. if loan_id is None:
  395. loans()
  396. print('Which loan would you like to pay back?')
  397. loan_id = input('Loan id:')
  398. if amount is None:
  399. print('How much would you like to pay back?')
  400. amount = input('Amount (type `all` for the remaining loan):')
  401. if amount != 'all':
  402. try:
  403. amount = float(amount) # this can also raise a ValueError
  404. if amount <= 0:
  405. raise ValueError
  406. except ValueError:
  407. print('Amount must be a number larger than 0 or \'all\' (for paying back the remaining loan).')
  408. return
  409. _fake_loading_bar('Transferring the money', duration=1.7)
  410. response = client_request('repay_loan', {"session_id": connection.session_id, 'amount': amount, 'loan_id': loan_id})
  411. success = 'message' in response and 'error' not in response
  412. if success:
  413. print(f'You repayed the specified amount of {CURRENCY_NAME}.')
  414. else:
  415. if 'error' in response:
  416. print('Repaying the loan failed with message:', response['error'])
  417. else:
  418. print('Repaying the loan failed.')
  419. def _global_variables():
  420. return client_request('global_variables')
  421. def orders_on(obj_name=None):
  422. if obj_name is None: # TODO list some available objects
  423. obj_name = input('Name of object to check: ')
  424. if obj_name == CURRENCY_NAME.replace(CURRENCY_SYMBOL, 'K'):
  425. obj_name = CURRENCY_NAME
  426. _fake_loading_bar('Loading Data', duration=2.3)
  427. response = client_request('orders_on', {"session_id": connection.session_id, "ownable": obj_name})
  428. success = 'data' in response
  429. if success:
  430. print(my_tabulate(response['data'],
  431. headers=['My', 'Buy?', 'Name', 'Size', 'Limit', 'Expires', 'No.'],
  432. tablefmt="pipe"))
  433. else:
  434. if 'error' in response:
  435. print('Order access failed with message:', response['error'])
  436. else:
  437. print('Order access failed.')
  438. def gift(username=None, obj_name=None, amount=None):
  439. if username is None:
  440. username = input('Username of recipient: ')
  441. if obj_name is None:
  442. obj_name = input('Name of object to give: ')
  443. if obj_name == CURRENCY_NAME.replace(CURRENCY_SYMBOL, 'K'):
  444. obj_name = CURRENCY_NAME
  445. if amount is None:
  446. amount = input('How many?: ')
  447. _fake_loading_bar('Sending Gift', duration=4.2)
  448. response = client_request('gift',
  449. {"session_id": connection.session_id,
  450. "username": username,
  451. "object_name": obj_name,
  452. "amount": amount})
  453. if 'error' in response:
  454. print('Order access failed with message:', response['error'])
  455. elif 'message' in response:
  456. print(response['message'])
  457. def news():
  458. _fake_loading_bar('Loading Data', duration=0.76)
  459. response = client_request('news', {})
  460. success = 'data' in response
  461. if success:
  462. print(my_tabulate(response['data'],
  463. headers=['Date', 'Title'],
  464. tablefmt="pipe"))
  465. else:
  466. if 'error' in response:
  467. print('News access failed with message:', response['error'])
  468. else:
  469. print('News access failed.')
  470. def tender_calendar():
  471. _fake_loading_bar('Loading Data', duration=0.76)
  472. response = client_request('tender_calendar', {})
  473. success = 'data' in response
  474. # preprocess
  475. table = response['data']
  476. for row in table:
  477. row[0] = datetime.fromtimestamp(row[0]).strftime("%Y-%m-%d %H:%M:%S")
  478. row[1] = f'{row[1]:8.2%}'
  479. row[2] = datetime.fromtimestamp(row[2]).strftime("%Y-%m-%d %H:%M:%S")
  480. if success:
  481. print(my_tabulate(table,
  482. headers=['Date', 'MRO', 'Runs until'],
  483. tablefmt="pipe"))
  484. else:
  485. if 'error' in response:
  486. print('Tender calendar access failed with message:', response['error'])
  487. else:
  488. print('Tender calendar access failed.')
  489. def tradables():
  490. _fake_loading_bar('Loading Data', duration=12.4)
  491. response = client_request('tradables', {})
  492. success = 'data' in response
  493. if success:
  494. print(my_tabulate(response['data'],
  495. headers=['Name', 'Course', 'Market Cap.'],
  496. tablefmt="pipe"))
  497. world_wealth = 0
  498. for row in response['data']:
  499. if row[2] is not None:
  500. world_wealth += row[2]
  501. print('Estimated worldwide wealth:', world_wealth)
  502. else:
  503. if 'error' in response:
  504. print('Data access failed with message:', response['error'])
  505. else:
  506. print('Data access failed.')
  507. def trades_on(obj_name=None, limit=5):
  508. limit = float(limit)
  509. if obj_name is None: # TODO list some available objects
  510. obj_name = input('Name of object to check: ')
  511. if obj_name == CURRENCY_NAME.replace(CURRENCY_SYMBOL, 'K'):
  512. obj_name = CURRENCY_NAME
  513. _fake_loading_bar('Loading Data', duration=0.34 * limit)
  514. response = client_request('trades_on', {"session_id": connection.session_id,
  515. "ownable": obj_name,
  516. "limit": limit})
  517. success = 'data' in response
  518. if success:
  519. print(my_tabulate(response['data'],
  520. headers=['Time', 'Volume', 'Price'],
  521. tablefmt="pipe"))
  522. else:
  523. if 'error' in response:
  524. print('Trades access failed with message:', response['error'])
  525. else:
  526. print('Trades access failed.')
  527. def trades(limit=10):
  528. limit = float(limit)
  529. _fake_loading_bar('Loading Data', duration=limit * 0.21)
  530. response = client_request('trades', {"session_id": connection.session_id,
  531. "limit": limit})
  532. success = 'data' in response
  533. if success:
  534. print(my_tabulate(response['data'],
  535. headers=['Buy?', 'Name', 'Volume', 'Price', 'Time'],
  536. tablefmt="pipe"))
  537. else:
  538. if 'error' in response:
  539. print('Trades access failed with message:', response['error'])
  540. else:
  541. print('Trades access failed.')
  542. def old_orders(include_canceled=None, include_executed=None, limit=10):
  543. limit = float(limit)
  544. if include_canceled is None:
  545. include_canceled = yn_dialog('Include canceled/expired orders in list?')
  546. if include_executed is None:
  547. include_executed = yn_dialog('Include fully executed orders in list?')
  548. _fake_loading_bar('Loading Data', duration=limit * 0.27)
  549. response = client_request('old_orders', {"session_id": connection.session_id,
  550. "include_canceled": include_canceled,
  551. "include_executed": include_executed,
  552. "limit": limit})
  553. success = 'data' in response
  554. if success:
  555. print(my_tabulate(response['data'],
  556. headers=['Buy?', 'Name', 'Size', 'Limit', 'Expiry', 'No.', 'Status'],
  557. tablefmt="pipe"))
  558. else:
  559. if 'error' in response:
  560. print('Order access failed with message:', response['error'])
  561. else:
  562. print('Order access failed.')
  563. def issue_bond(name=None, coupon=None, run_time=None):
  564. if name is None:
  565. name = input('Name of the bond:')
  566. if not re.fullmatch(OWNABLE_NAME_PATTERN, name):
  567. print(f'Invalid name: {name}. Name must match {OWNABLE_NAME_PATTERN} and must not be copyright-protected '
  568. f'(yes, we have an upload filter for that).')
  569. if coupon is None:
  570. coupon = input('Coupon (e.g. 0.005 for 0.5% p.a.):')
  571. if run_time is None:
  572. run_time = input('Run-time of the bond (minutes):')
  573. _fake_loading_bar('Checking name', duration=3.7)
  574. _fake_loading_bar('Publishing important information', duration=0.3)
  575. response = client_request('issue_bond', {"session_id": connection.session_id,
  576. "name": name,
  577. "coupon": coupon,
  578. "run_time": run_time})
  579. if 'error' in response:
  580. print('Issuing bond failed with message:', response['error'])
  581. elif 'message' in response:
  582. print(response['message'])
  583. # noinspection PyShadowingBuiltins
  584. def exit():
  585. global exiting
  586. exiting = True
  587. def _fake_loading_bar(msg, duration=5.):
  588. if len(msg) >= 60:
  589. raise AssertionError('Loading bar label too large')
  590. msg += ': '
  591. print(msg, end='')
  592. sys.stdout.flush()
  593. bar_length = 79 - len(msg)
  594. for _ in range(bar_length):
  595. if not debug:
  596. time.sleep(duration / bar_length)
  597. print('#', end='')
  598. sys.stdout.flush()
  599. print('\n', end='')
  600. sys.stdout.flush()