def _to_returns(cls, augmented: pd.DataFrame) -> Dict[str, Optional[float]]: returns = dict() lookbacks = properties.get("fund.lookbacks") dt = augmented.last_valid_index() for lookback in lookbacks: window = augmented[dt - pd_offset_from_lookback(lookback): dt]["value"] returns[lookback] = (window.iat[-1] - window.iat[0]) / window.iat[0] \ if len(window.index) \ else None return returns
def get_all_indicators() -> List[Indicator]: from .ppo import PPO from .mdd import MDD from .num_breakouts import NumBreakouts from .sharpe_ratio import SharpeRatio from .stability import Stability from .returns import MaxReturns, MinReturns from .rsi import RSI lookbacks = properties.get("fund.lookbacks") return [MDD(), MDT(), NumBreakouts(), PPO(), RSI(), SharpeRatio(), Stability()] \ + [MaxReturns(lookback) for lookback in lookbacks] \ + [MinReturns(lookback) for lookback in lookbacks]
def calc_fees(funds: List[Fund]) -> pd.DataFrame: platform_charge = properties.get("fund.fees.platform.charge") fees = pd.DataFrame( [[fund.ocf, fund.amc, fund.entryCharge, fund.exitCharge, fund.bidAskSpread, platform_charge, sum(filter(None, [fund.entryCharge, fund.exitCharge, fund.bidAskSpread])), sum(filter(None, [fund.ocf, platform_charge]))] for fund in funds], index=[fund.isin for fund in funds], columns=["ocf", "amc", "entry_charge", "exit_charge", "bid_ask_spread", "platform_charge", "total_one_off_fees", "total_annual_fees"] ) return fees
def test_get_from_environment(): sys_path = properties.get("PATH") assert isinstance(sys_path, str) assert sys_path
def test_get_missing_property(): assert properties.get("missing.property") is None
def test_parse_json(): assert properties.get("fund.fees.platform.charge") == 0.0035 assert properties.get("fund.lookbacks") == [ "5Y", "3Y", "1Y", "6M", "3M", "1M", "2W", "1W", "3D", "1D"]
def test_get_from_file(): assert properties.get("heroku.app.name") == "fund-analyzer-compute"
import falcon from waitress import serve from lib.util import properties from server.middleware.logging import LoggingMiddleware from server.routes.home_routes import home_routes from server.routes.indicators_routes import indicators_routes from server.routes.simulate_routes import simulate_routes app = falcon.API(middleware=[LoggingMiddleware()]) app.add_route("/", home_routes) app.add_route("/indicators/fund", indicators_routes, suffix="fund") app.add_route("/indicators/stock", indicators_routes, suffix="stock") app.add_route("/simulate", simulate_routes) app.add_route("/simulate/predict", simulate_routes, suffix="predict") app.add_route("/simulate/strategies", simulate_routes, suffix="strategies") if __name__ == "__main__": serve(app, port=properties.get("server.default.port"))
from datetime import date from typing import Callable, List, Optional import pandas as pd from ffn import calc_sharpe from pandas.tseries.frequencies import to_offset from lib.fund.fund import Fund from lib.util import properties DAILY_PLATFORM_FEES = (1 + properties.get("fund.fees.platform.charge")) ** (1 / 252) - 1 def calc_returns(prices_df: pd.DataFrame, dt: date, duration: pd.DateOffset, fees_df: pd.DataFrame) -> pd.Series: window = prices_df[dt - duration: dt] # type: ignore returns_before_fees = (window.iloc[-1] - window.iloc[0]) / window.iloc[0] if len(window) else 0 num_bdays = len(window.index) - 1 prorated_annual_fees = (1 + fees_df["total_annual_fees"]) ** (num_bdays / 252) - 1 prorated_total_fees = fees_df["total_one_off_fees"] + prorated_annual_fees returns_after_fees = returns_before_fees - prorated_total_fees return returns_after_fees def calc_fees(funds: List[Fund]) -> pd.DataFrame: platform_charge = properties.get("fund.fees.platform.charge") fees = pd.DataFrame( [[fund.ocf, fund.amc, fund.entryCharge, fund.exitCharge, fund.bidAskSpread, platform_charge, sum(filter(None, [fund.entryCharge, fund.exitCharge, fund.bidAskSpread])), sum(filter(None, [fund.ocf, platform_charge]))] for fund in funds],
from typing import Dict, Iterator, Optional import requests import ujson from lib.util import properties DATA_HOST = properties.get("client.data") def _remove_leading_slash(endpoint: str) -> str: return endpoint[1:] if endpoint.startswith("/") else endpoint def get(endpoint: str, params: Optional[Dict[str, str]] = None) -> object: endpoint = f"{DATA_HOST}/{_remove_leading_slash(endpoint)}" return requests.get(endpoint, params).json() def post(endpoint: str, data: Optional[object] = None) -> object: endpoint = f"{DATA_HOST}/{_remove_leading_slash(endpoint)}" return requests.post(endpoint, json=data) def stream(endpoint: str, data: Optional[object] = None) -> Iterator[Dict]: endpoint = f"{DATA_HOST}/{_remove_leading_slash(endpoint)}" line_seps = {b",", b"[", b"]"} lines = requests.post(endpoint, json=data, stream=True).iter_lines() return (ujson.loads(line) for line in lines if line not in line_seps)
}, { "date": "2017-04-26T00:00:00Z", "price": 482.0 }, { "date": "2017-04-30T00:00:00Z", "price": 482.0 }, { "date": "2017-05-01T00:00:00Z", "price": 482.0 }, { "date": "2017-05-02T00:00:00Z", "price": 482.0 }]) @pytest.mark.parametrize("lookback", properties.get("fund.lookbacks")) def test_empty_returns_nan(lookback: str): assert np.isnan( MinReturns(lookback).calc(historic_prices=pd.Series()).value) assert np.isnan( MaxReturns(lookback).calc(historic_prices=pd.Series()).value) @pytest.mark.parametrize("lookback,expected_max,expected_min", [ ("1D", 0.01, -0.02), ("3D", 0.02, -0.03), ("1W", 0.03, -0.04), ("2W", 0.04, -0.04), ("1M", 0.02, -0.02), ("3M", np.nan, np.nan), ("6M", np.nan, np.nan),