Coverage for src/scrilla/main.py: 46%
611 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-18 18:14 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-07-18 18:14 +0000
1# This file is part of scrilla: https://github.com/chinchalinchin/scrilla.
3# scrilla is free software: you can redistribute it and/or modify
4# it under the terms of the GNU General Public License version 3
5# as published by the Free Software Foundation.
7# scrilla is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU General Public License for more details.
12# You should have received a copy of the GNU General Public License
13# along with scrilla. If not, see <https://www.gnu.org/licenses/>
14# or <https://github.com/chinchalinchin/scrilla/blob/develop/main/LICENSE>.
16"""
17# CLI Entrypoint
18This script acts as the entrypoint for the CLI application and contains the majority of the control structures for the program. It parses the arguments supplied through the command line, delegates them to the appropriate application function and then passes the results to the `scrilla.util.outputter` module functions for formatting and printing to screen.
20The arguments are parsed in such a way that arguments which are not supplied are set to None. All application functions are set up to accept None as a value for their optional arguments. This makes passing arguments to application functions easier as the `main.py` script doesn't have to worry about their values. In other words, `main.py` always passes all arguments to application functions, even if they aren't supplied through the command line; it just sets the ones which aren't supplied to None.
21"""
23import time
24from datetime import date
25from typing import Callable, Dict, List, Union
27from scrilla import settings, files, cache
28from scrilla.static import definitions
29from scrilla.util.errors import InputValidationError
30from scrilla.static import formats
31from scrilla.util.outputter import Logger
33# TODO: conditional imports based on value of ANALYSIS_MODE
35logger = Logger('main', settings.LOG_LEVEL)
38def validate_function_usage(selection: str, args: List[str], wrapper_function: Callable, required_length: int = 1, exact: bool = False) -> None:
39 """
40 Parameters
41 ----------
42 1. **selection** : ``str``
43 2. **args**: ``list``
44 List of ticker/statistic symbols whose length is be to validated.
45 3. **wrapper_function** : ``func()``
46 4. **required_length** : ``int``
47 The number of tickers required for the function.
48 5. **exact** : ``bool``
49 *Optional*. If the required length constraint is an equality, set to `True`. If the constraint is an inequality, set to `False`. Defaults to `False`.
50 """
52 start_time = time.time()
53 if(not exact and (len(args) > (required_length-1))) or (exact and (len(args) == required_length)): 53 ↛ 55line 53 didn't jump to line 55, because the condition on line 53 was never false
54 wrapper_function()
55 elif exact:
56 raise InputValidationError(
57 f'Invalid number of arguments for \'{selection}\' function. Function requires {required_length} arguments.')
58 else:
59 raise InputValidationError(
60 f'Invalid number of arguments for \'{selection}\' function. Function requires more than {required_length} arguments.')
61 end_time = time.time()
62 logger.info(
63 f'Total execution time: {end_time - start_time}s', 'validate_function_usage')
66def print_format_to_screen(args: Dict[str, Union[str, date, float, None, bool]]) -> bool:
67 """
68 Checks if the inputted optional arguments allow printing pretty formatted text to screen.
70 Parameters
71 ----------
72 1. **args** : ``dict``
73 Formatted dictionary containing optional arguments. Result of a call to `helper.format_xtra_args_dict`.
75 Returns
76 -------
77 ``bool``
78 """
79 return not args['json'] and not args['suppress_output']
82def print_json_to_screen(args: Dict[str, Union[str, date, float, None, bool]]) -> bool:
83 """
84 Checks if inputted optional arguments allow printing json formatted text to screen.
86 Parameters
87 ----------
88 1. **args** : ``dict``
89 Formatted dictionary containing optional arguments. Result of a call to `helper.format_xtra_args_dict`.
91 Returns
92 -------
93 ``bool``
94 """
95 return args['json'] and not args['suppress_output']
98def do_program(cli_args: List[str]) -> None:
99 """
100 Parses command line arguments and passes the formatted arguments to appropriate function from the library.
101 """
102 files.init_static_data()
103 cache.init_cache()
105 args = formats.format_args(cli_args, settings.ESTIMATION_METHOD)
106 exact, selected_function = False, None
108 # START CLI FUNCTION DEFINITIONS
110 # NO ARGUMENT FUNCTIONS
111 # FUNCTION: Help Message
112 if args['function_arg'] in definitions.FUNC_DICT["help"]['values']:
113 def cli_help():
114 from scrilla.util.outputter import help_msg
115 # NOTE: in this case, the arguments are function names, not tickers.
116 # it may be behoove the application to rejigger the argparse
117 # just so names are consistent with what is represented.
118 help_msg(function_filter=args['tickers'])
119 selected_function, required_length = cli_help, 0
121 # FUNCTION: Clear Cache
122 elif args['function_arg'] in definitions.FUNC_DICT["clear_cache"]['values']:
123 def cli_clear_cache():
124 logger.info(f'Clearing {settings.CACHE_DIR}', 'do_program')
125 files.clear_cache()
126 selected_function, required_length = cli_clear_cache, 0
128 # FUNCTION: Clear Static
129 elif args['function_arg'] in definitions.FUNC_DICT["clear_static"]['values']:
130 def cli_clear_static():
131 logger.info(f'Clearing {settings.STATIC_DIR}', 'do_program')
132 files.clear_directory(directory=settings.STATIC_DIR, retain=True)
133 selected_function, required_length = cli_clear_static, 0
135 # FUNCTION: Clear Common
136 elif args['function_arg'] in definitions.FUNC_DICT["clear_common"]['values']:
137 def cli_clear_common():
138 logger.info(f'Clearing {settings.COMMON_DIR}', 'do_program')
139 files.clear_directory(directory=settings.COMMON_DIR, retain=True)
140 selected_function, required_length = cli_clear_common, 0
142 # FUNCTION: Print Stock Watchlist
143 elif args['function_arg'] in definitions.FUNC_DICT['list_watchlist']['values']: 143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true
144 def cli_watchlist():
145 from scrilla.util.outputter import title_line, print_list
146 tickers = files.get_watchlist()
147 title_line("Watchlist")
148 print_list(tickers)
149 selected_function, required_length = cli_watchlist, 0
151 # FUNCTION: Purge Data Directories
152 elif args['function_arg'] in definitions.FUNC_DICT["purge"]['values']:
153 def cli_purge():
154 from scrilla.files import clear_directory, clear_cache
155 logger.info(
156 f'Clearing {settings.STATIC_DIR}, {settings.CACHE_DIR} and {settings.COMMON_DIR}', 'do_program')
157 files.clear_directory(directory=settings.STATIC_DIR, retain=True)
158 files.clear_directory(directory=settings.COMMON_DIR, retain=True)
159 files.clear_cache()
160 selected_function, required_length = cli_purge, 0
162 # FUNCTION: Display Version
163 elif args['function_arg'] in definitions.FUNC_DICT["version"]['values']:
164 def cli_version():
165 from scrilla.settings import APP_DIR
166 from os.path import join
167 version_file = join(APP_DIR, 'version.txt')
168 with open(version_file, 'r') as f:
169 print(f.read())
170 selected_function, required_length = cli_version, 0
172 # FUNCTION: Yield Curve
173 elif args['function_arg'] in definitions.FUNC_DICT['yield_curve']['values']:
174 def cli_yield_curve():
175 from scrilla.static.keys import keys
176 from scrilla.services import get_daily_interest_history
177 yield_curve = {}
178 # TODO: this is inefficient. get_daily_interest_history should be modified
179 # to return all maturities if no maturity is specified. otherwise, this is
180 # duplicating a ton of operations
181 for maturity in keys['YIELD_CURVE']:
182 curve_rate = get_daily_interest_history(maturity=maturity,
183 start_date=args['start_date'],
184 end_date=args['start_date'])
186 yield_curve[maturity] = curve_rate[list(
187 curve_rate.keys())[0]]/100
189 if print_format_to_screen(args): 189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true
190 from scrilla.util.outputter import scalar_result
191 scalar_result(calculation=maturity,
192 result=yield_curve[maturity], currency=False)
194 if print_json_to_screen(args): 194 ↛ 198line 194 didn't jump to line 198, because the condition on line 194 was never false
195 from json import dumps
196 print(dumps(yield_curve))
198 if args['save_file'] is not None: 198 ↛ 199line 198 didn't jump to line 199, because the condition on line 198 was never true
199 from scrilla.files import save_file
200 save_file(file_to_save=yield_curve,
201 file_name=args['save_file'])
202 selected_function, required_length = cli_yield_curve, 0
204 # ARGUMENT FUNCTIONS
205 # FUNCTION: Asset Grouping
207 elif args['function_arg'] in definitions.FUNC_DICT['asset_type']['values']:
208 def cli_asset_type():
209 from scrilla.files import get_asset_type
210 from scrilla.util.outputter import string_result
211 for arg in args['tickers']:
212 asset_type = get_asset_type(arg)
213 string_result(f'asset_type({arg})', asset_type)
214 selected_function, required_length = cli_asset_type, 1
216 # FUNCTION: Black-Scholes Value At Risk
217 elif args['function_arg'] in definitions.FUNC_DICT['var']['values']:
218 def cli_var():
219 from scrilla.analysis.models.geometric.statistics import calculate_risk_return
220 from scrilla.analysis.models.geometric.probability import percentile
221 from scrilla.services import get_daily_price_history
222 from scrilla.util.helper import get_first_json_key
223 from scrilla.static.keys import keys
225 all_vars = {}
226 for arg in args['tickers']:
227 prices = get_daily_price_history(ticker=arg,
228 start_date=args['start_date'],
229 end_date=args['end_date'])
230 latest_price = prices[get_first_json_key(
231 prices)][keys['PRICES']['CLOSE']]
232 profile = calculate_risk_return(ticker=arg,
233 sample_prices=prices,
234 method=args['estimation_method'])
235 valueatrisk = percentile(S0=latest_price,
236 vol=profile['annual_volatility'],
237 ret=profile['annual_return'],
238 expiry=args['expiry'],
239 prob=args['probability'])
241 all_vars[arg] = {keys['STATISTICS']['VAR']: valueatrisk}
243 if print_format_to_screen(args): 243 ↛ 244line 243 didn't jump to line 244, because the condition on line 243 was never true
244 from scrilla.util.outputter import scalar_result
245 scalar_result(f'{arg}_VaR', valueatrisk)
247 if print_json_to_screen(args): 247 ↛ 251line 247 didn't jump to line 251, because the condition on line 247 was never false
248 from json import dumps
249 print(dumps(all_vars))
251 if args['save_file'] is not None:
252 from scrilla.files import save_file
253 save_file(file_to_save=all_vars, file_name=args['save_file'])
255 selected_function, required_length = cli_var, 1
257 # FUNCTION: Black-Scholes Conditional Value At Risk
258 elif args['function_arg'] in definitions.FUNC_DICT['cvar']['values']:
259 def cli_cvar():
260 from scrilla.services import get_daily_price_history
261 from scrilla.static.keys import keys
262 from scrilla.analysis.models.geometric.statistics import calculate_risk_return
263 from scrilla.analysis.models.geometric.probability import percentile, conditional_expected_value
264 from scrilla.util.helper import get_first_json_key
265 all_cvars = {}
266 for arg in args['tickers']:
267 prices = get_daily_price_history(ticker=arg,
268 start_date=args['start_date'],
269 end_date=args['end_date'])
270 latest_price = prices[get_first_json_key(
271 prices)][keys['PRICES']['CLOSE']]
272 profile = calculate_risk_return(ticker=arg,
273 sample_prices=prices,
274 method=args['estimation_method'])
275 valueatrisk = percentile(S0=latest_price,
276 vol=profile['annual_volatility'],
277 ret=profile['annual_return'],
278 expiry=args['expiry'],
279 prob=args['probability'])
280 cvar = conditional_expected_value(S0=latest_price,
281 vol=profile['annual_volatility'],
282 ret=profile['annual_return'],
283 expiry=args['expiry'],
284 conditional_value=valueatrisk)
285 all_cvars[arg] = {keys['STATISTICS']['CVAR']: cvar}
287 if print_format_to_screen(args): 287 ↛ 288line 287 didn't jump to line 288, because the condition on line 287 was never true
288 from scrilla.util.outputter import scalar_result
289 scalar_result(
290 f'{arg}_conditional_VaR', cvar)
292 if print_json_to_screen(args): 292 ↛ 296line 292 didn't jump to line 296, because the condition on line 292 was never false
293 from json import dumps
294 print(dumps(all_cvars))
296 if args['save_file'] is not None:
297 from scrilla.files import save_file
298 save_file(file_to_save=all_cvars, file_name=args['save_file'])
300 selected_function, required_length = cli_cvar, 1
302 # FUNCTION: Capital Asset Pricing Model Cost of Equity
303 elif args['function_arg'] in definitions.FUNC_DICT['capm_equity_cost']['values']:
304 def cli_capm_equity_cost():
305 from scrilla.analysis.markets import cost_of_equity
306 from scrilla.static.keys import keys
307 all_costs = {}
308 for arg in args['tickers']:
309 equity_cost = cost_of_equity(ticker=arg,
310 start_date=args['start_date'],
311 end_date=args['end_date'],
312 method=args['estimation_method'])
313 all_costs[arg] = {keys['STATISTICS']['EQUITY']: equity_cost}
315 if print_format_to_screen(args): 315 ↛ 316line 315 didn't jump to line 316, because the condition on line 315 was never true
316 from scrilla.util.outputter import scalar_result
317 scalar_result(f'{arg}_equity_cost',
318 equity_cost, currency=False)
320 if print_json_to_screen(args): 320 ↛ 324line 320 didn't jump to line 324, because the condition on line 320 was never false
321 from json import dumps
322 print(dumps(all_costs))
324 if args['save_file'] is not None:
325 from scrilla.files import save_file
326 save_file(file_to_save=all_costs, file_name=args['save_file'])
328 selected_function, required_length = cli_capm_equity_cost, 1
330 # FUNCTION: Capital Asset Pricing Model Beta
331 elif args['function_arg'] in definitions.FUNC_DICT['capm_beta']['values']:
332 def cli_capm_beta():
333 from scrilla.analysis.markets import market_beta
334 from scrilla.static.keys import keys
335 all_betas = {}
336 for arg in args['tickers']:
337 beta = market_beta(ticker=arg,
338 start_date=args['start_date'],
339 end_date=args['end_date'],
340 method=args['estimation_method'])
341 all_betas[arg] = {keys['STATISTICS']['BETA']: beta}
343 if print_format_to_screen(args): 343 ↛ 344line 343 didn't jump to line 344, because the condition on line 343 was never true
344 from scrilla.util.outputter import scalar_result
345 scalar_result(f'{arg}_beta', beta, currency=False)
347 if print_json_to_screen(args): 347 ↛ 351line 347 didn't jump to line 351, because the condition on line 347 was never false
348 from json import dumps
349 print(dumps(all_betas))
351 if args['save_file'] is not None:
352 from scrilla.files import save_file
353 save_file(file_to_save=all_betas, file_name=args['save_file'])
354 selected_function, required_length = cli_capm_beta, 1
356 # FUNCTION: Last Close Price
357 elif args['function_arg'] in definitions.FUNC_DICT["close"]['values']:
358 def cli_close():
359 from scrilla.services import get_daily_price_latest
361 all_prices = {}
362 for arg in args['tickers']:
363 price = get_daily_price_latest(arg)
364 all_prices[arg] = price
366 if print_format_to_screen(args): 366 ↛ 367line 366 didn't jump to line 367, because the condition on line 366 was never true
367 from scrilla.util.outputter import scalar_result
368 scalar_result(
369 calculation=f'Last {arg} close price', result=float(price))
371 if print_json_to_screen(args): 371 ↛ 375line 371 didn't jump to line 375, because the condition on line 371 was never false
372 from json import dumps
373 print(dumps(all_prices))
375 if args['save_file'] is not None:
376 from scrilla.files import save_file
377 save_file(file_to_save=all_prices, file_name=args['save_file'])
378 selected_function, required_length = cli_close, 1
380 # FUNCTION: Correlation Matrix
381 elif args['function_arg'] in definitions.FUNC_DICT["correlation"]['values']:
382 def cli_correlation():
383 from scrilla.static.keys import keys
384 from scrilla.analysis.models.geometric.statistics import correlation_matrix
386 if args['estimation_method'] == keys['ESTIMATION']['LIKE']:
387 logger.info(
388 'This calculation takes a while, strap in...', 'do_program')
390 matrix = correlation_matrix(tickers=args['tickers'],
391 start_date=args['start_date'],
392 end_date=args['end_date'],
393 method=args['estimation_method'])
395 if print_format_to_screen(args):
396 from scrilla.util.outputter import correlation_matrix as correlation_output
397 correlation_output(
398 tickers=args['tickers'], correl_matrix=matrix)
400 elif print_json_to_screen(args): 400 ↛ 404line 400 didn't jump to line 404, because the condition on line 400 was never false
401 from json import dumps
402 print(dumps({'correlation_matrix': matrix}))
404 if args['save_file'] is not None:
405 from scrilla.files import save_file
406 save_file(file_to_save=matrix, file_name=args['save_file'])
408 selected_function, required_length = cli_correlation, 2
410 # FUNCTION: Correlation Time Series
411 elif args['function_arg'] in definitions.FUNC_DICT['correlation_time_series']['values']: 411 ↛ 412line 411 didn't jump to line 412, because the condition on line 411 was never true
412 def cli_correlation_series():
413 from scrilla.analysis.models.geometric.statistics import calculate_moment_correlation_series
414 if print_format_to_screen(args):
415 from scrilla.util.outputter import scalar_result
417 logger.info(
418 'This calculation takes a while, strap in...', 'do_program')
419 ticker_1, ticker_2 = args['tickers'][0], args['tickers'][1]
420 result = calculate_moment_correlation_series(ticker_1=ticker_1,
421 ticker_2=ticker_2,
422 start_date=args['start_date'],
423 end_date=args['end_date'])
424 if print_format_to_screen(args):
425 for this_date in result:
426 scalar_result(calculation=f'{this_date}_{ticker_1}_{ticker_2}_correlation',
427 result=float(result[this_date]), currency=False)
428 elif print_json_to_screen(args):
429 from json import dumps
430 print(dumps(result))
432 if args['save_file'] is not None:
433 from scrilla.files import save_file
434 save_file(file_to_save=result, file_name=args['save_file'])
435 selected_function, required_length, exact = cli_correlation_series, 2, True
437 # FUNCTION: Discount Dividend Model
438 elif args['function_arg'] in definitions.FUNC_DICT["discount_dividend"]['values']:
439 def cli_discount_dividend():
440 from scrilla.services import get_dividend_history
441 from scrilla.analysis.objects.cashflow import Cashflow
442 from scrilla.static.keys import keys
443 model_results = {}
445 for arg in args['tickers']:
446 dividends = get_dividend_history(arg)
447 if args['discount'] is None:
448 from scrilla.analysis.markets import cost_of_equity
449 discount = cost_of_equity(
450 ticker=arg, method=args['estimation_method'])
451 else:
452 discount = args['discount']
453 result = Cashflow(sample=dividends,
454 discount_rate=discount).calculate_net_present_value()
455 model_results[arg] = {keys['MODELS']['DDM']: result}
457 if print_format_to_screen(args): 457 ↛ 458line 457 didn't jump to line 458, because the condition on line 457 was never true
458 from scrilla.util.outputter import scalar_result
459 scalar_result(f'Net Present Value ({arg} dividends)',
460 model_results[arg][keys['MODELS']['DDM']])
462 if print_json_to_screen(args): 462 ↛ 466line 462 didn't jump to line 466, because the condition on line 462 was never false
463 from json import dumps
464 print(dumps(model_results))
466 if args['save_file'] is not None: 466 ↛ 467line 466 didn't jump to line 467, because the condition on line 466 was never true
467 from scrilla.files import save_file
468 save_file(file_to_save=model_results,
469 file_name=args['save_file'])
470 selected_function, required_length = cli_discount_dividend, 1
472 elif args['function_arg'] in definitions.FUNC_DICT['dividends']['values']:
473 def cli_dividends():
474 from scrilla.services import get_dividend_history
475 if print_format_to_screen(args): 475 ↛ 476line 475 didn't jump to line 476, because the condition on line 475 was never true
476 from scrilla.util.outputter import scalar_result
478 all_dividends = {}
479 for arg in args['tickers']:
480 dividends = get_dividend_history(arg)
481 all_dividends[arg] = dividends
483 if print_format_to_screen(args): 483 ↛ 484line 483 didn't jump to line 484, because the condition on line 483 was never true
484 for this_date in dividends:
485 scalar_result(
486 calculation=f'{arg}_dividend({this_date})', result=dividends[this_date])
488 if print_json_to_screen(args): 488 ↛ exitline 488 didn't return from function 'cli_dividends', because the condition on line 488 was never false
489 from json import dumps
490 print(dumps(all_dividends))
492 selected_function, required_length = cli_dividends, 1
494 # FUNCTION: Efficient Frontier
495 elif args['function_arg'] in definitions.FUNC_DICT['efficient_frontier']['values']:
496 def cli_efficient_frontier():
497 from scrilla.analysis.objects.portfolio import Portfolio
498 from scrilla.analysis.optimizer import calculate_efficient_frontier
500 portfolio = Portfolio(tickers=args['tickers'],
501 start_date=args['start_date'],
502 end_date=args['end_date'],
503 method=args['estimation_method'])
504 frontier = calculate_efficient_frontier(portfolio=portfolio,
505 steps=args['steps'])
507 if args['investment'] is not None: 507 ↛ 508line 507 didn't jump to line 508, because the condition on line 507 was never true
508 from scrilla.services import get_daily_prices_latest
509 prices = get_daily_prices_latest(
510 tickers=args['tickers'])
511 else:
512 prices = None
514 if print_format_to_screen(args): 514 ↛ 515line 514 didn't jump to line 515, because the condition on line 514 was never true
515 from scrilla.util.outputter import efficient_frontier as frontier_output
516 frontier_output(portfolio=portfolio,
517 frontier=frontier,
518 investment=args['investment'],
519 latest_prices=prices)
520 if print_json_to_screen(args): 520 ↛ 527line 520 didn't jump to line 527, because the condition on line 520 was never false
521 from json import dumps
522 print(dumps(formats.format_frontier(portfolio=portfolio,
523 frontier=frontier,
524 investment=args['investment'],
525 latest_prices=prices)))
527 if args['save_file'] is not None: 527 ↛ 528line 527 didn't jump to line 528, because the condition on line 527 was never true
528 from scrilla.files import save_frontier
529 save_frontier(portfolio=portfolio,
530 frontier=frontier,
531 investment=args['investment'],
532 file_name=args['save_file'],
533 latest_prices=prices)
534 selected_function, required_length = cli_efficient_frontier, 2
536 # FUNCTION: Maximize Portfolio Return
537 elif args['function_arg'] in definitions.FUNC_DICT['maximize_return']['values']:
538 def cli_maximize_return():
539 from scrilla.analysis.objects.portfolio import Portfolio
540 from scrilla.analysis.optimizer import maximize_portfolio_return
542 portfolio = Portfolio(tickers=args['tickers'],
543 start_date=args['start_date'],
544 end_date=args['end_date'],
545 method=args['estimation_method'])
547 allocation = maximize_portfolio_return(
548 portfolio=portfolio)
550 if args['investment'] is not None: 550 ↛ 551line 550 didn't jump to line 551, because the condition on line 550 was never true
551 from scrilla.services import get_daily_prices_latest
552 prices = get_daily_prices_latest(
553 tickers=args['tickers'])
554 else:
555 prices = None
557 if print_format_to_screen(args): 557 ↛ 558line 557 didn't jump to line 558, because the condition on line 557 was never true
558 from scrilla.util.outputter import optimal_result
559 optimal_result(portfolio=portfolio,
560 allocation=allocation,
561 investment=args['investment'],
562 latest_prices=prices)
564 if print_json_to_screen(args): 564 ↛ 571line 564 didn't jump to line 571, because the condition on line 564 was never false
565 from json import dumps
566 print(dumps(formats.format_allocation(allocation=allocation,
567 portfolio=portfolio,
568 investment=args['investment'],
569 latest_prices=prices)))
571 if args['save_file'] is not None: 571 ↛ 572line 571 didn't jump to line 572, because the condition on line 571 was never true
572 from scrilla.files import save_allocation
573 save_allocation(allocation=allocation,
574 portfolio=portfolio,
575 file_name=args['save_file'],
576 investment=args['investment'],
577 latest_prices=prices)
578 selected_function, required_length = cli_maximize_return, 2
580 # FUNCTION: Moving Averages of Logarithmic Returns
581 elif args['function_arg'] in definitions.FUNC_DICT['moving_averages']['values']: 581 ↛ 582line 581 didn't jump to line 582, because the condition on line 581 was never true
582 def cli_moving_averages():
583 from scrilla.analysis.models.geometric.statistics import calculate_moving_averages
584 # TODO: moving averages with estimation techniques
586 moving_averages = calculate_moving_averages(ticker=args['tickers'][0],
587 start_date=args['start_date'],
588 end_date=args['end_date'],
589 method=args['estimation_method'])
591 if print_format_to_screen(args):
592 from scrilla.util.outputter import moving_average_result
593 moving_average_result(
594 ticker=args['tickers'][0], averages=moving_averages)
596 if print_json_to_screen(args):
597 from json import dumps
598 print(dumps(moving_averages))
600 if args['save_file'] is not None:
601 from scrilla.files import save_file
602 save_file(file_to_save=moving_averages,
603 file_name=args['save_file'])
605 selected_function, required_length = cli_moving_averages, 1
607 # FUNCTION: Optimize Portfolio Variance/Volatility
608 elif args['function_arg'] in definitions.FUNC_DICT['optimize_portfolio']['values']: 608 ↛ 609line 608 didn't jump to line 609, because the condition on line 608 was never true
609 def cli_optimize_portfolio_variance():
610 from scrilla.analysis.objects.portfolio import Portfolio
612 portfolio = Portfolio(tickers=args['tickers'],
613 start_date=args['start_date'],
614 end_date=args['end_date'],
615 method=args['estimation_method'])
617 if args['optimize_sharpe']:
618 from scrilla.analysis.optimizer import maximize_sharpe_ratio
619 allocation = maximize_sharpe_ratio(
620 portfolio=portfolio, target_return=args['target'])
621 else:
622 from scrilla.analysis.optimizer import optimize_portfolio_variance
623 allocation = optimize_portfolio_variance(
624 portfolio=portfolio, target_return=args['target'])
626 if args['investment'] is not None:
627 from scrilla.services import get_daily_prices_latest
628 prices = get_daily_prices_latest(
629 tickers=args['tickers'])
630 else:
631 prices = None
633 if print_format_to_screen(args):
634 from scrilla.util.outputter import optimal_result
635 optimal_result(
636 portfolio=portfolio,
637 allocation=allocation,
638 investment=args['investment'],
639 latest_prices=prices)
641 if print_json_to_screen(args):
642 from json import dumps
643 print(dumps(formats.format_allocation(allocation=allocation,
644 portfolio=portfolio,
645 investment=args['investment'],
646 latest_prices=prices)))
648 if args['save_file'] is not None:
649 from scrilla.files import save_allocation
650 save_allocation(allocation=allocation,
651 portfolio=portfolio,
652 file_name=args['save_file'],
653 investment=args['investment'],
654 latest_prices=prices)
656 selected_function, required_length = cli_optimize_portfolio_variance, 2
658 # FUNCTION: Optimize Portfolio Conditional Value At Risk
659 elif args['function_arg'] in definitions.FUNC_DICT['optimize_portfolio_conditional_var']['values']: 659 ↛ 660line 659 didn't jump to line 660, because the condition on line 659 was never true
660 def cli_optimize_conditional_value_at_risk():
661 from scrilla.analysis.optimizer import optimize_conditional_value_at_risk
662 from scrilla.analysis.objects.portfolio import Portfolio
664 portfolio = Portfolio(tickers=args['tickers'],
665 start_date=args['start_date'],
666 end_date=args['end_date'],
667 method=args['estimation_method'])
668 allocation = optimize_conditional_value_at_risk(portfolio=portfolio,
669 prob=args['probability'],
670 expiry=args['expiry'],
671 target_return=args['target'])
672 if args['investment'] is not None:
673 from scrilla.services import get_daily_prices_latest
674 prices = get_daily_prices_latest(
675 tickers=args['tickers'])
676 else:
677 prices = None
679 if print_format_to_screen(args):
680 from scrilla.util.outputter import optimal_result
681 optimal_result(portfolio=portfolio,
682 allocation=allocation,
683 investment=args['investment'],
684 latest_prices=prices)
686 if print_json_to_screen(args):
687 from json import dumps
688 print(dumps(formats.format_allocation(allocation=allocation,
689 portfolio=portfolio,
690 investment=args['investment'],
691 latest_prices=prices)))
693 if args['save_file'] is not None:
694 from scrilla.files import save_allocation
695 save_allocation(allocation=allocation,
696 portfolio=portfolio,
697 file_name=args['save_file'],
698 investment=args['investment'],
699 latest_prices=prices)
700 selected_function, required_length = cli_optimize_conditional_value_at_risk, 2
702 # FUNCTION: Plot Correlation Time Series
703 elif args['function_arg'] in definitions.FUNC_DICT['plot_correlation']['values']: 703 ↛ 704line 703 didn't jump to line 704, because the condition on line 703 was never true
704 def cli_plot_correlation():
705 from scrilla.analysis.models.geometric.statistics import calculate_moment_correlation_series
706 from scrilla.analysis.plotter import plot_correlation_series
708 logger.info(
709 'This calculation takes a while, strap in...', 'do_program')
711 correlation_history = calculate_moment_correlation_series(ticker_1=args['tickers'][0],
712 ticker_2=args['tickers'][1],
713 start_date=args['start_date'],
714 end_date=args['end_date'])
715 plot_correlation_series(tickers=args['tickers'],
716 series=correlation_history,
717 savefile=args['save_file'])
719 selected_function, required_length, exact = cli_plot_correlation, 2, True
721 # FUNCTION: Plot Dividend History With Linear Regression Model
722 elif args['function_arg'] in definitions.FUNC_DICT['plot_dividends']['values']: 722 ↛ 723line 722 didn't jump to line 723, because the condition on line 722 was never true
723 def cli_plot_dividends():
724 from scrilla.services import get_dividend_history
725 from scrilla.analysis.objects.cashflow import Cashflow
726 from scrilla.analysis.plotter import plot_cashflow
728 dividends = get_dividend_history(ticker=args['tickers'][0])
729 if args['discount'] is None:
730 from scrilla.analysis.markets import cost_of_equity
731 args['discount'] = cost_of_equity(ticker=args['tickers'][0],
732 method=args['estimation_method'])
733 div_cashflow = Cashflow(sample=dividends,
734 discount_rate=args['discount'])
735 plot_cashflow(ticker=args['tickers'][0],
736 cashflow=div_cashflow, show=True,
737 savefile=args['save_file'])
739 selected_function, required_length, exact = cli_plot_dividends, 1, True
741 # FUNCTION: Plot Efficient Frontier
742 elif args['function_arg'] in definitions.FUNC_DICT['plot_frontier']['values']: 742 ↛ 743line 742 didn't jump to line 743, because the condition on line 742 was never true
743 def cli_plot_frontier():
744 from scrilla.analysis.objects.portfolio import Portfolio
745 from scrilla.analysis.optimizer import calculate_efficient_frontier
746 from scrilla.analysis.plotter import plot_frontier
748 portfolio = Portfolio(tickers=args['tickers'],
749 start_date=args['start_date'],
750 end_date=args['end_date'],
751 method=args['estimation_method'])
753 frontier = calculate_efficient_frontier(portfolio=portfolio,
754 steps=args['steps'])
756 plot_frontier(portfolio=portfolio,
757 frontier=frontier,
758 show=True,
759 savefile=args['save_file'])
761 selected_function, required_length = cli_plot_frontier, 2
763 # FUNCTION: Plot Moving Averages of Logarithmic Returns
764 elif args['function_arg'] in definitions.FUNC_DICT['plot_moving_averages']['values']: 764 ↛ 765line 764 didn't jump to line 765, because the condition on line 764 was never true
765 def cli_plot_moving_averages():
766 from scrilla.analysis.models.geometric.statistics import calculate_moving_averages
767 from scrilla.analysis.plotter import plot_moving_averages
768 # TODO: estimation techniques with moving averages
769 moving_averages = calculate_moving_averages(ticker=args['tickers'][0],
770 start_date=args['start_date'],
771 end_date=args['end_date'],
772 method=args['estimation_method'])
774 plot_moving_averages(ticker=args['tickers'][0],
775 averages=moving_averages,
776 show=True, savefile=args['save_file'])
778 selected_function, required_length, exact = cli_plot_moving_averages, 1, True
780 # FUNCTION: Plot Return QQ Series
781 elif args['function_arg'] in definitions.FUNC_DICT['plot_return_qq']['values']: 781 ↛ 782line 781 didn't jump to line 782, because the condition on line 781 was never true
782 def cli_plot_qq_returns():
783 from scrilla.analysis.models.geometric.statistics import get_sample_of_returns
784 from scrilla.analysis.estimators import qq_series_for_sample
785 from scrilla.analysis.plotter import plot_qq_series
787 returns = get_sample_of_returns(ticker=args['tickers'][0],
788 start_date=args['start_date'],
789 end_date=args['end_date'],
790 daily=True)
792 qq_series = qq_series_for_sample(sample=returns)
794 plot_qq_series(ticker=args['tickers'][0],
795 qq_series=qq_series,
796 show=True,
797 savefile=args['save_file'])
799 selected_function, required_length, exact = cli_plot_qq_returns, 1, True
801 # FUNCTION: Plot Histogram of Returns
802 elif args['function_arg'] in definitions.FUNC_DICT['plot_return_dist']['values']: 802 ↛ 803line 802 didn't jump to line 803, because the condition on line 802 was never true
803 def cli_plot_dist_returns():
804 from scrilla.analysis.models.geometric.statistics import get_sample_of_returns
805 from scrilla.analysis.plotter import plot_return_histogram
807 returns = get_sample_of_returns(ticker=args['tickers'][0],
808 start_date=args['start_date'],
809 end_date=args['end_date'],
810 daily=True)
811 plot_return_histogram(ticker=args['tickers'][0],
812 sample=returns,
813 show=True,
814 savefile=args['save_file'])
815 selected_function, required_length, exact = cli_plot_dist_returns, 1, True
817 # FUNCTION: Plot Risk-Return Profile
818 elif args['function_arg'] in definitions.FUNC_DICT['plot_risk_profile']['values']: 818 ↛ 819line 818 didn't jump to line 819, because the condition on line 818 was never true
819 def cli_plot_risk_profile():
820 from scrilla.analysis.models.geometric.statistics import calculate_risk_return
821 from scrilla.analysis.plotter import plot_profiles
822 from scrilla.util.dater import format_date_range
823 profiles = {}
824 for arg in args['tickers']:
825 profiles[arg] = calculate_risk_return(ticker=arg,
826 start_date=args['start_date'],
827 end_date=args['end_date'],
828 method=args['estimation_method'])
830 plot_profiles(symbols=args['tickers'],
831 show=True,
832 profiles=profiles,
833 savefile=args['save_file'],
834 subtitle=format_date_range(start_date=args['start_date'],
835 end_date=args['end_date']))
836 selected_function, required_length = cli_plot_risk_profile, 1
838 elif args['function_arg'] in definitions.FUNC_DICT['plot_yield_curve']['values']: 838 ↛ 839line 838 didn't jump to line 839, because the condition on line 838 was never true
839 def cli_plot_yield_curve():
840 from scrilla.util.dater import get_next_business_date, to_string
841 from scrilla.static.keys import keys
842 from scrilla.services import get_daily_interest_history
843 from scrilla.analysis.plotter import plot_yield_curve
844 yield_curve = {}
845 args['start_date'] = get_next_business_date(args['start_date'])
846 start_date_string = to_string(args['start_date'])
847 yield_curve[start_date_string] = []
848 for maturity in keys['YIELD_CURVE']:
849 rate = get_daily_interest_history(maturity=maturity,
850 start_date=args['start_date'],
851 end_date=args['start_date'])
852 yield_curve[start_date_string].append(rate[start_date_string])
854 plot_yield_curve(yield_curve=yield_curve,
855 show=True,
856 savefile=args['save_file'])
857 selected_function, required_length, exact = cli_plot_yield_curve, 0, True
858 # FUNCTION: Price History
859 elif args['function_arg'] in definitions.FUNC_DICT['price_history']['values']: 859 ↛ 860line 859 didn't jump to line 860, because the condition on line 859 was never true
860 def cli_price_history():
861 from scrilla.services import get_daily_price_history
862 from scrilla.static.keys import keys
863 if print_format_to_screen(args):
864 from scrilla.util.outputter import scalar_result
866 all_prices = {}
867 for arg in args['tickers']:
868 prices = get_daily_price_history(ticker=arg,
869 start_date=args['start_date'],
870 end_date=args['end_date'])
871 all_prices[arg] = {}
872 for this_date in prices:
873 price = prices[this_date][keys['PRICES']['CLOSE']]
874 all_prices[arg][this_date] = price
876 if print_format_to_screen(args):
877 scalar_result(
878 calculation=f'{arg}({this_date})', result=float(price))
880 if print_json_to_screen(args):
881 from json import dumps
882 print(dumps(all_prices))
884 if args['save_file'] is not None:
885 from scrilla.files import save_file
886 save_file(file_to_save=all_prices, file_name=args['save_file'])
887 selected_function, required_length = cli_price_history, 1
889 # FUNCTION: Interest Rate History
890 elif args['function_arg'] in definitions.FUNC_DICT['interest_history']['values']: 890 ↛ 891line 890 didn't jump to line 891, because the condition on line 890 was never true
891 def cli_interest_history():
892 from scrilla.services import get_daily_interest_history
893 if print_format_to_screen(args):
894 from scrilla.util.outputter import scalar_result
896 all_rates = {}
897 for arg in args['tickers']:
898 all_rates[arg] = get_daily_interest_history(maturity=arg,
899 start_date=args['start_date'],
900 end_date=args['end_date'])
901 for this_date in all_rates[arg]:
902 all_rates[arg][this_date] = all_rates[arg][this_date]
904 if print_format_to_screen(args):
905 for this_date in all_rates[arg]:
906 scalar_result(calculation=f'{arg}_YIELD({this_date})', result=float(
907 all_rates[arg][this_date])/100, currency=False)
909 if print_json_to_screen(args):
910 from json import dumps
911 print(dumps(all_rates))
913 if args['save_file'] is not None:
914 from scrilla.files import save_file
915 save_file(file_to_save=all_rates,
916 file_name=args['save_file'])
917 selected_function, required_length = cli_interest_history, 1
919 # FUNCTION: Risk Free Rate
920 elif args['function_arg'] in definitions.FUNC_DICT['risk_free_rate']['values']: 920 ↛ 921line 920 didn't jump to line 921, because the condition on line 920 was never true
921 def cli_risk_free_rate():
922 from scrilla.services import get_risk_free_rate
923 from scrilla.settings import RISK_FREE_RATE
924 rate = {}
925 rate[RISK_FREE_RATE] = get_risk_free_rate()
927 if print_format_to_screen(args):
928 from scrilla.util.outputter import title_line, scalar_result
929 title_line("Risk Free Rate")
930 scalar_result(calculation=formats.formats['RISK_FREE_TITLE'].format(RISK_FREE_RATE),
931 result=rate[RISK_FREE_RATE], currency=False)
932 if print_json_to_screen(args):
933 from json import dumps
934 print(dumps(rate))
936 if args['save_file'] is not None:
937 from scrilla.files import save_file
938 save_file(file_to_save=rate, file_name=args['save_file'])
939 selected_function, required_length, exact = cli_risk_free_rate, 0, True
941 # FUNCTION: Risk-Return Profile
942 elif args['function_arg'] in definitions.FUNC_DICT["risk_profile"]['values']: 942 ↛ 983line 942 didn't jump to line 983, because the condition on line 942 was never false
943 def cli_risk_return():
944 from scrilla.analysis.models.geometric.statistics import calculate_risk_return
945 from scrilla.analysis.markets import sharpe_ratio, market_beta, cost_of_equity
946 profiles = {}
947 for arg in args['tickers']:
948 profiles[arg] = calculate_risk_return(ticker=arg,
949 method=args['estimation_method'],
950 start_date=args['start_date'],
951 end_date=args['end_date'])
952 profiles[arg]['sharpe_ratio'] = sharpe_ratio(ticker=arg,
953 start_date=args['start_date'],
954 end_date=args['end_date'],
955 ticker_profile=profiles[arg],
956 method=args['estimation_method'])
957 profiles[arg]['asset_beta'] = market_beta(ticker=arg,
958 start_date=args['start_date'],
959 end_date=args['end_date'],
960 ticker_profile=profiles[arg],
961 method=args['estimation_method'])
962 profiles[arg]['equity_cost'] = cost_of_equity(ticker=arg,
963 start_date=args['start_date'],
964 end_date=args['end_date'],
965 ticker_profile=profiles[arg],
966 method=args['estimation_method'])
968 if print_format_to_screen(args): 968 ↛ 969line 968 didn't jump to line 969, because the condition on line 968 was never true
969 from scrilla.util.outputter import risk_profile
970 risk_profile(profiles=profiles)
972 if print_json_to_screen(args): 972 ↛ 976line 972 didn't jump to line 976, because the condition on line 972 was never false
973 from json import dumps
974 print(dumps(profiles))
976 if args['save_file'] is not None:
977 from scrilla.files import save_file
978 save_file(file_to_save=profiles, file_name=args['save_file'])
980 selected_function, required_length = cli_risk_return, 1
982 # FUNCTION: Model Discount Screener
983 elif args['function_arg'] in definitions.FUNC_DICT["screener"]['values']:
984 def cli_screener():
985 from scrilla.analysis.markets import screen_for_discount
986 from scrilla.static.keys import keys
987 from scrilla.util.outputter import screen_results
988 if args['model'] is None:
989 model = keys['MODELS']['DDM']
990 results = screen_for_discount(
991 model=model, discount_rate=args['discount'])
992 screen_results(info=results, model=model)
993 selected_function, required_length = cli_screener, 0
995 # FUNCTION: Sharpe Ratio
996 elif args['function_arg'] in definitions.FUNC_DICT["sharpe_ratio"]['values']:
997 def cli_sharpe_ratio():
998 from scrilla.analysis.markets import sharpe_ratio
999 all_results = {}
1000 for arg in args['tickers']:
1001 result = sharpe_ratio(ticker=arg,
1002 start_date=args['start_date'],
1003 end_date=args['end_date'],
1004 method=args['estimation_method'])
1005 all_results[arg] = result
1007 if print_format_to_screen(args):
1008 from scrilla.util.outputter import scalar_result
1009 scalar_result(calculation=f'{arg}_sharpe_ratio', result=result,
1010 currency=False)
1012 if print_json_to_screen(args):
1013 from json import dumps
1014 print(dumps(all_results))
1016 if args['save_file'] is not None:
1017 from scrilla.files import save_file
1018 save_file(file_to_save=all_results,
1019 file_name=args['save_file'])
1020 selected_function, required_length = cli_sharpe_ratio, 1
1022 # FUNCTION: Store Key
1023 elif args['function_arg'] in definitions.FUNC_DICT['store']['values']:
1024 def cli_store():
1025 from scrilla.files import set_credentials
1026 set_credentials(value=args['value'], which_key=args['key'])
1027 selected_function, required_length = cli_store, 0
1029 # FUNCTION: Get Latest Economic Statistic
1030 elif args['function_arg'] in definitions.FUNC_DICT["statistic"]['values']:
1031 def cli_statistic():
1032 from scrilla.services import get_daily_fred_latest
1033 all_stats = {}
1034 for stat in args['tickers']:
1035 result = get_daily_fred_latest(symbol=stat)
1036 all_stats[stat] = result
1038 if print_format_to_screen(args):
1039 from scrilla.util.outputter import scalar_result
1040 scalar_result(calculation=stat,
1041 result=result, currency=False)
1043 if print_json_to_screen(args):
1044 from json import dumps
1045 print(dumps(all_stats))
1047 if args['save_file'] is not None:
1048 from scrilla.files import save_file
1049 save_file(file_to_save=all_stats, file_name=args['save_file'])
1051 selected_function, required_length = cli_statistic, 1
1053 # FUNCTION: Statistic History
1054 elif args['function_arg'] in definitions.FUNC_DICT['statistic_history']['values']:
1055 def cli_statistic_history():
1056 from scrilla.services import get_daily_fred_history
1057 if print_format_to_screen(args):
1058 from scrilla.util.outputter import scalar_result
1060 all_stats = {}
1061 for arg in args['tickers']:
1062 stats = get_daily_fred_history(symbol=arg,
1063 start_date=args['start_date'],
1064 end_date=args['end_date'])
1065 all_stats[arg] = stats
1066 if print_format_to_screen(args):
1067 for this_date in stats:
1068 scalar_result(calculation=f'{arg}({this_date})',
1069 result=stats[this_date],
1070 currency=False)
1072 if print_json_to_screen(args):
1073 from json import dumps
1074 print(dumps(all_stats))
1076 if args['save_file'] is not None:
1077 from scrilla.files import save_file
1078 save_file(file_to_save=all_stats, file_name=args['save_file'])
1079 selected_function, required_length = cli_statistic_history, 1
1081 # FUNCTION: Set Watchlist
1082 elif args['function_arg'] in definitions.FUNC_DICT["watchlist"]['values']:
1083 def cli_watchlist():
1084 files.add_watchlist(new_tickers=args['tickers'])
1085 logger.info(
1086 "Watchlist saved. Use -ls option to print watchlist.", 'do_program')
1087 selected_function, required_length = cli_watchlist, 1
1089 else:
1090 def cli_help():
1091 from scrilla.util.outputter import help_msg
1092 help_msg()
1093 selected_function, required_length = cli_help, 0
1095 # END CLI FUNCTION DEFINITIONS
1097 if selected_function is not None: 1097 ↛ exitline 1097 didn't return from function 'do_program', because the condition on line 1097 was never false
1098 validate_function_usage(selection=args['function_arg'],
1099 args=args['tickers'],
1100 wrapper_function=selected_function,
1101 required_length=required_length,
1102 exact=exact)
1105def scrilla():
1106 import sys
1108 do_program(sys.argv[1:])
1111if __name__ == "__main__": 1111 ↛ 1112line 1111 didn't jump to line 1112, because the condition on line 1111 was never true
1112 scrilla()