Skip to content

Commit

Permalink
move static settings to static.py and use settings.py for dynamic con…
Browse files Browse the repository at this point in the history
…figuration
  • Loading branch information
chinchalinchin committed Sep 13, 2021
1 parent 7cbab99 commit 11747fa
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 61 deletions.
9 changes: 5 additions & 4 deletions src/scrilla/analysis/optimizer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import scipy.optimize as optimize
from scrilla import static

import settings
import util.outputter as outputter
Expand Down Expand Up @@ -41,7 +42,7 @@ def optimize_portfolio_variance(portfolio, target_return=None):
portfolio_constraints = equity_constraint

allocation = optimize.minimize(fun = portfolio.volatility_function, x0 = init_guess,
method=settings.OPTIMIZATION_METHOD, bounds=equity_bounds,
method=static.constants['OPTIMIZATION_METHOD'], bounds=equity_bounds,
constraints=portfolio_constraints, options={'disp': False})

return allocation.x
Expand Down Expand Up @@ -80,7 +81,7 @@ def optimize_conditional_value_at_risk(portfolio, prob, expiry, target_return=No

allocation = optimize.minimize(fun = lambda x: portfolio.conditional_value_at_risk_function(x, expiry, prob),
x0 = init_guess,
method=settings.OPTIMIZATION_METHOD, bounds=equity_bounds,
method=static.constants['OPTIMIZATION_METHOD'], bounds=equity_bounds,
constraints=portfolio_constraints, options={'disp': False})

return allocation.x
Expand Down Expand Up @@ -122,7 +123,7 @@ def maximize_sharpe_ratio(portfolio, target_return=None):

allocation = optimize.minimize(fun = lambda x: (-1)*portfolio.sharpe_ratio_function(x),
x0 = init_guess,
method=settings.OPTIMIZATION_METHOD, bounds=equity_bounds,
method=static.constants['OPTIMIZATION_METHOD'], bounds=equity_bounds,
constraints=portfolio_constraints, options={'disp': False})

return allocation.x
Expand All @@ -148,7 +149,7 @@ def maximize_portfolio_return(portfolio):

logger.debug(f'Maximizing {tickers} Portfolio Return')
allocation = optimize.minimize(fun = lambda x: (-1)*portfolio.return_function(x),
x0 = init_guess, method=settings.OPTIMIZATION_METHOD,
x0 = init_guess, method=static.constants['OPTIMIZATION_METHOD'],
bounds=equity_bounds, constraints=equity_constraint,
options={'disp': False})

Expand Down
12 changes: 6 additions & 6 deletions src/scrilla/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def get_static_data(static_type):
else:
path = settings.STATIC_TICKERS_FILE

elif static_type == settings.STAT_ECON:
elif static_type == static.keys['ASSETS']['STAT']:
if static_econ_blob:
blob = static_econ_blob
else:
Expand All @@ -214,7 +214,7 @@ def get_static_data(static_type):
static_crypto_blob = symbols
elif static_type == static.keys['ASSETS']['EQUITY']:
static_tickers_blob = symbols
elif static_type == settings.STAT_ECON:
elif static_type == static.keys['ASSETS']['STAT']:
static_econ_blob = symbols
return symbols

Expand Down Expand Up @@ -341,11 +341,11 @@ def format_allocation(allocation, portfolio, investment=None):
for j, item in enumerate(portfolio.tickers):
holding = {}
holding['ticker'] = item
holding['allocation'] = round(allocation[j], settings.ACCURACY)
holding['allocation'] = round(allocation[j], static.constants['ACCURACY'])
if investment is not None:
holding['shares'] = float(shares[j])
holding['annual_return'] = round(portfolio.mean_return[j], settings.ACCURACY)
holding['annual_volatility'] = round(portfolio.sample_vol[j], settings.ACCURACY)
holding['annual_return'] = round(portfolio.mean_return[j], static.constants['ACCURACY'])
holding['annual_volatility'] = round(portfolio.sample_vol[j], static.constants['ACCURACY'])
allocation_format.append(holding)

json_format = {}
Expand Down Expand Up @@ -450,7 +450,7 @@ def clear_directory(directory, retain=True):

for f in filelist:
filename = os.path.basename(f)
if retain and filename == settings.KEEP_EXT:
if retain and filename == static.constants['KEEP_FILE']:
continue
os.remove(os.path.join(directory, f))

Expand Down
3 changes: 2 additions & 1 deletion src/scrilla/objects/cashflow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
from scrilla import static
import util.helper as helper
import util.outputter as outputter

Expand Down Expand Up @@ -216,7 +217,7 @@ def calculate_net_present_value(self):

self.NPV += self.get_growth_function(current_time) / ((1 + self.discount_rate)**current_time)

if self.NPV - previous_value < settings.NPV_DELTA_TOLERANCE:
if self.NPV - previous_value < static.keys['NPV_DELTA_TOLERANCE']:
calculating = False
i += 1

Expand Down
14 changes: 7 additions & 7 deletions src/scrilla/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(self, type):
self.type = type

def construct_url(self, symbol, start_date, end_date):
if self.type == static.keys['SERVICE']['STATISTICS']['QUANDL']:
if self.type == static.keys['SERVICES']['STATISTICS']['QUANDL']['MANAGER']:
url = f'{settings.Q_URL}/'
query = f'{settings.PATH_Q_FRED}/{symbol}?'

Expand All @@ -39,7 +39,7 @@ def get_stats(self, symbol, start_date, end_date):
url = self.construct_url(symbol, start_date, end_date)
response = requests.get(url).json()

if self.type == static.keys['SERVICE']['STATISTICS']['QUANDL']:
if self.type == static.keys['SERVICES']['STATISTICS']['QUANDL']['MANAGER']:

raw_stat = response[settings.Q_FIRST_LAYER][settings.Q_SECOND_LAYER]
formatted_stat = {}
Expand Down Expand Up @@ -154,13 +154,13 @@ def __init__(self, type):
self.type = type

def construct_url(self, ticker, asset_type):
if self.type == static.keys['SERVICE']['PRICES']['ALPHA_VANTAGE']:
if self.type == static.keys['SERVICES']['PRICES']['ALPHA_VANTAGE']['MANAGER']:
query = f'{settings.PARAM_AV_TICKER}={ticker}'

if asset_type == static.keys['ASSETS']['EQUITY']:
query += f'&{settings.PARAM_AV_FUNC}={settings.ARG_AV_FUNC_EQUITY_DAILY}'
elif asset_type == static.keys['ASSETS']['CRYPTO']:
query += f'&{settings.PARAM_AV_FUNC}={settings.ARG_AV_FUNC_CRYPTO_DAILY}&{settings.PARAM_AV_DENOM}={settings.DENOMINATION}'
query += f'&{settings.PARAM_AV_FUNC}={settings.ARG_AV_FUNC_CRYPTO_DAILY}&{settings.PARAM_AV_DENOM}={static.constants["DENOMINATION"]}'

# NOTE: only need to modify EQUITY query, CRYPTO always returns full history
if (asset_type == static.keys['ASSETS']['EQUITY']):
Expand All @@ -178,7 +178,7 @@ def get_prices(self, ticker, start_date, end_date, asset_type):
url = self.construct_url(ticker, asset_type)
response = requests.get(url).json()

if self.type == static.keys['SERVICE']['PRICES']['ALPHA_VANTAGE']:
if self.type == static.keys['SERVICES']['PRICES']['ALPHA_VANTAGE']['MANAGER']:
first_element = helper.get_first_json_key(response)
# end function is daily rate limit is reached
if first_element == settings.AV_RES_DAY_LIMIT:
Expand All @@ -197,7 +197,7 @@ def get_prices(self, ticker, start_date, end_date, asset_type):
else:
logger.debug('Waiting.')

time.sleep(settings.BACKOFF_PERIOD)
time.sleep(static.constants['BACKOFF_PERIOD'])
response = requests.get(url).json()
first_element = helper.get_first_json_key(response)

Expand All @@ -210,7 +210,7 @@ def get_prices(self, ticker, start_date, end_date, asset_type):

def slice_prices(self, start_date, end_date, asset_type, prices):
# NOTE: only really needed for `alpha_vantage` responses so far, due to the fact AlphaVantage either returns everything or 100 days or prices.
if self.type == static.keys['SERVICE']['PRICES']['ALPHA_VANTAGE']:
if self.type == static.keys['SERVICES']['PRICES']['ALPHA_VANTAGE']['MANAGER']:
# NOTE: Remember AlphaVantage is ordered current to earliest. END_INDEX is
# actually the beginning of slice and START_INDEX is actually end of slice.
try:
Expand Down
40 changes: 3 additions & 37 deletions src/scrilla/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os, sys, dotenv, json
from scrilla import static

APP_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.dirname(APP_DIR)
Expand Down Expand Up @@ -27,32 +28,21 @@ def __init__(self, message):
3. APP_DIR: Folder containing this file. \n \n
4. APP_ENV: Application environment. \n \n
5. LOG_LEVEL: Debug output level. \n \n
6. CONFIG_FILE: Location of secondary credentials file. \n \n
7. CACHE_DIR: Folder where cached price histories reside. \n \n
8. CACHE_PRO_KEY: File name where risk-profile calculations are saved. \n \n
9. FILE_EXT: File extension used in CACHE_DIR. \n \n
10. STATIC_DIR: Folder where static data reside. \n \n
11. FILE_EXT: File extension used in STATIC_DIR. \n \n
12. STATIC_TICKERS_FILE: File containing a list of all equity ticker symbols with price histories that can be retrieved from external services. \n \n
13. STATIC_ECON_FILE: File containg a list of all economic statistics with sample histories that can be retrieved from external services. \n \n
14. STATIC_CRYPTO_FILE: File containing a list of all crypto ticker symbols with price histories that can be retrieved from external services. \n \n
15. ACCURACY: Number of decimals place saved in calculations. \n \n
18. GUI_WIDTH: Width of root widget in GUI. \n \n
19. GUI_HEIGHT: Height of root widget in GUI. \n \n
20. OPTIMIZATION_METHOD: Scipy method used to optimize portfolios. \n \n
21. INVESTMENT_MODE: Determines if output is percentage or absolute. \n \n
22. FRONTIER_STEPS: Number of points in efficient frontier output. \n \n
23. MA_1_PERIOD: Number of days in first moving average period. \n \n
24. MA_2_PERIOD: Number of days in second moving average period. \n \n
25. MA_3_PERIOD: Number of days in first moving average period. \n \n
26. ONE_TRADING_DAY: Length of trading day in years. \n \n
27. PRICE_YEAR_CUTOFF: Earliest year considered in price histories. \n \n
28. DENOMINATION: Denomination in which prices are quoted. \n \n
29. NPV_DELTA_TOLERANCE: NPV calculations stop when the next value adds less than this amount. \n \n
29. RISK_FREE_RATE: Interest rate used for cashflow valuations. \n \n
30. MARKET_PROXY: Ticker symbol used to calculate market rate of return
30. STAT_ECON: Constant for economic statistics. \n \n
31. INIT: Flag to initialize STATIC_DIR \n \n
32. PRICE_MANAGER: Service in charge of price histories. \n \n
33. STAT_MANAGER: Service in charge of statistic histories. \n \n
34. DIVIDEND_MANAGER: Service in charge of dividend payment histories. \n \n
Expand Down Expand Up @@ -93,8 +83,6 @@ def __init__(self, message):

APP_NAME="scrilla"

VERSION="0.0.1"

APP_ENV = os.environ.setdefault('APP_ENV', 'local')

# NOTE: Load in local.env file if not running application container. Container should
Expand All @@ -108,18 +96,10 @@ def __init__(self, message):

# TODO: CACHE only supports JSON currently. Future file extensions: csv and txt.
FILE_EXT = os.environ.setdefault("FILE_EXT", "json")
KEEP_EXT = ".gitkeep"

CACHE_DIR = os.path.join(APP_DIR, 'data', 'cache')
CACHE_SQLITE_FILE = os.path.join(CACHE_DIR, 'scrilla.db')

CACHE_PRO_KEY="profile"
CACHE_PRICE_KEY="prices"
CACHE_COR_KEY="correlation"
CACHE_DIV_KEY="dividends"
CACHE_STAT_KEY="statistic"
CACHE_EQUITY_KEY="equity_statistic"

STATIC_DIR = os.path.join(APP_DIR, 'data', 'static')

STATIC_TICKERS_FILE = os.path.join(STATIC_DIR, f'tickers.{FILE_EXT}')
Expand All @@ -129,8 +109,6 @@ def __init__(self, message):
COMMON_DIR=os.path.join(APP_DIR, 'data', 'common')
COMMON_WATCHLIST_FILE=os.path.join(COMMON_DIR, f'watchlist.{FILE_EXT}')

ACCURACY, BACKOFF_PERIOD=5, 30

# See .sample.env for more information.
LOCAL_CACHE = os.environ.setdefault('LOCAL_CACHE_ENABLED', 'true').strip().lower() == 'true'

Expand All @@ -150,9 +128,6 @@ def __init__(self, message):
os.environ['GUI_HEIGHT'] = '800'

## FINANCIAL ALGORITHM CONFIGURATION

OPTIMIZATION_METHOD="SLSQP"

try:
FRONTIER_STEPS = int(os.environ.setdefault('FRONTIER_STEPS', '5'))
except (ValueError, TypeError) as ParseError:
Expand Down Expand Up @@ -195,15 +170,6 @@ def __init__(self, message):
DEFAULT_ANALYSIS_PERIOD=100
os.environ['DEFAULT_ANALYSIS_PERIOD']=100

# Number of days

# TODO: candidates for static.py
PRICE_YEAR_CUTOFF=1950
DENOMINATION = "USD"
NPV_DELTA_TOLERANCE = 0.0000001
STAT_ECON="statistic"


# SEE: ARG_Q_YIELD_CURVE for allowabled values
RISK_FREE_RATE=os.environ.setdefault("RISK_FREE", "10-Year").strip("\"")

Expand Down Expand Up @@ -240,8 +206,8 @@ def __init__(self, message):
AV_RES_EQUITY_KEY="symbol"
AV_RES_CRYPTO_FIRST_LAYER='Time Series (Digital Currency Daily)'
AV_RES_CRYPTO_KEY="currency code"
AV_RES_CRYPTO_CLOSE_PRICE=f'4a. close ({DENOMINATION})'
AV_RES_CRYPTO_OPEN_PRICE=f'1a. open ({DENOMINATION})'
AV_RES_CRYPTO_CLOSE_PRICE=f'4a. close ({static.constants["DENOMINATION"]})'
AV_RES_CRYPTO_OPEN_PRICE=f'1a. open ({static.constants["DENOMINATION"]})'
AV_RES_ERROR='Error Message'
AV_RES_LIMIT='Note'
AV_RES_DAY_LIMIT='Information'
Expand Down
27 changes: 21 additions & 6 deletions src/scrilla/static.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
},
'ASSETS':{
'EQUITY': 'equity',
'CRYPTO': 'crypto'
'CRYPTO': 'crypto',
'STAT': 'statistics'
},
'CACHE':{
'PRICES': 'prices',
Expand All @@ -28,23 +29,37 @@
'START':'start_date',
'END':'end_date'
},
'SERVICE':{
'SERVICES':{
'PRICES':{
'ALPHA_VANTAGE': 'alpha_vantage',
'ALPHA_VANTAGE': {
'MANAGER': 'alpha_vantage'
}
},
'STATISTICS': {
'QUANDL': 'quandl'
'QUANDL': {
'MANAGER': 'quandl'
}
},
'DIVIDENDS': {
'IEX': 'iex'
'IEX': {
'MANAGER': 'iex'
}
}
}
}
constants = {
'ONE_TRADING_DAY': {
'EQUITY': (1/252),
'CRYPTO': (1/365)
}
},
'ACCURACY': 5,
'BACKOFF_PERIOD': 30,
'KEEP_FILE': '.gitkeep',
'PRICE_YEAR_CUTOFF': 1950,
'DENOMINATION': 'USD',
'NPV_DELTA_TOLERANCE': 0.0000001,
'OPTIMIZATION_METHOD': "SLSQP"

}

def get_trading_period(asset_type):
Expand Down

0 comments on commit 11747fa

Please sign in to comment.