def transactions_fx(): # Gets the transaction table and fills with fx information # Note that it uses the currency exchange for the date of transaction # Get all transactions from Specter and format # SPECTER ============================================ df = specter_df() if not df.empty: df['trade_date'] = pd.to_datetime(df['trade_date']) df = df.set_index('trade_date') # Ignore times in df to merge - keep only dates df.index = df.index.floor('d') df.index.rename('date', inplace=True) # SQL DATABASE ======================================== # Get all transactions from db and format df_sql = pd.read_sql_table('trades', current_app.db.engine) if not df_sql.empty: df_sql = df_sql[(df_sql.user_id == current_user.username)] # df = df[(df.trade_operation == "B") | (df.trade_operation == "S")] df_sql['trade_date'] = pd.to_datetime(df_sql['trade_date']) df_sql = df_sql.set_index('trade_date') # Ignore times in df to merge - keep only dates df_sql.index = df_sql.index.floor('d') df_sql.index.rename('date', inplace=True) # Merge both df = df.append(df_sql, sort=False) if df.empty: logging.warning("Transactions_FX - No txs found") return df # The current fx needs no conversion, set to 1 df[fx_rate()['fx_rate']] = 1 # Need to get currencies into the df in order to normalize # let's load a list of currencies needed and merge list_of_fx = df.trade_currency.unique().tolist() # loop through currency list for currency in list_of_fx: if currency == fx_rate()['fx_rate']: continue # Make a price request df[currency] = df.apply(find_fx, axis=1) # Now create a cash value in the preferred currency terms df['fx'] = df.apply(lambda x: x[x['trade_currency']], axis=1) df['cash_value_fx'] = df['cash_value'].astype(float) / df['fx'].astype( float) df['trade_fees_fx'] = df['trade_fees'].astype(float) / df['fx'].astype( float) df['trade_price_fx'] = df['trade_price'].astype(float) / df['fx'].astype( float) if 'trade_date' not in df.columns: df['trade_date'] = pd.to_datetime(df.index) return (df)
def navchart(): data = generatenav() navchart = data[["NAV_fx"]].copy() # dates need to be in Epoch time for Highcharts navchart.index = (navchart.index - datetime(1970, 1, 1)).total_seconds() navchart.index = navchart.index * 1000 navchart.index = navchart.index.astype(np.int64) navchart = navchart.to_dict() navchart = navchart["NAV_fx"] port_value_chart = data[[ "PORT_cash_value_fx", "PORT_fx_pos", "PORT_ac_CFs_fx" ]].copy() port_value_chart["ac_pnl_fx"] = (port_value_chart["PORT_fx_pos"] - port_value_chart["PORT_ac_CFs_fx"]) # dates need to be in Epoch time for Highcharts port_value_chart.index = (port_value_chart.index - datetime(1970, 1, 1)).total_seconds() port_value_chart.index = port_value_chart.index * 1000 port_value_chart.index = port_value_chart.index.astype(np.int64) port_value_chart = port_value_chart.to_dict() return render_template("warden/warden_navchart.html", title="NAV Historical Chart", navchart=navchart, port_value_chart=port_value_chart, fx=current_app.settings['PORTFOLIO']['base_fx'], current_user=fx_rate(), data=data, current_app=current_app)
def config_ini(): from config import Config config_file = Config.config_file if not os.path.isfile(config_file): flash('Config File not Found. Restart the app.', 'danger') config_contents = None else: f = open(config_file, 'r') config_contents = f.read() if request.method == 'POST': config_txt = request.form.get('config_txt') f = open(config_file, "w") f.write(config_txt) f.close() flash("Config File Saved", "success") config_contents = config_txt return render_template('warden/config_ini.html', title="Custom Configurations", current_app=current_app, current_user=fx_rate(), config_file=config_file, config_contents=config_contents )
def show_broadcast(): category = request.args.get("category") return render_template('warden/show_broadcast.html', title="Message Broadcaster", current_app=current_app, current_user=fx_rate(), category=category, )
def trade_transactions(): transactions = Trades.query.filter_by(user_id=current_user.username) if transactions.count() == 0: form = TradeForm() form.trade_currency.data = current_app.fx['code'] form.trade_date.data = datetime.utcnow() return render_template("warden/empty_txs.html", title="Empty Transaction List", current_app=current_app, current_user=fx_rate(), form=form) return render_template("warden/trade_transactions.html", title="Transaction History", transactions=transactions, current_app=current_app, current_user=fx_rate())
def heatmap(): heatmap_gen, heatmap_stats, years, cols = heatmap_generator() return render_template("warden/heatmap.html", title="Monthly Returns HeatMap", heatmap=heatmap_gen, heatmap_stats=heatmap_stats, years=years, cols=cols, current_app=current_app, current_user=fx_rate())
def realtime_btc(): try: fx_details = fx_rate() fx_r = { 'cross': fx_details['symbol'], 'fx_rate': fx_details['fx_rate'] } fx_r['btc_usd'] = realtime_price("BTC", fx='USD')['price'] fx_r['btc_fx'] = fx_r['btc_usd'] * fx_r['fx_rate'] except Exception as e: logging.warn( f"There was an error while getting realtime prices. Error: {e}") fx_r = 0 return json.dumps(fx_r)
def positions_json(): # Get all transactions and cost details # This serves the main page try: dfdyn, piedata = positions_dynamic() btc_price = realtime_price("BTC")['price'] * fx_rate()['fx_rate'] dfdyn = dfdyn.to_dict(orient='index') except Exception: dfdyn = piedata = None btc_price = 0 try: btc = realtime_price("BTC")['price'] except TypeError: btc = 0 if not btc: btc = 0 json_dict = { 'positions': dfdyn, 'piechart': piedata, 'user': current_app.fx, 'btc': btc_price } return simplejson.dumps(json_dict, ignore_nan=True)
def delalltrades(): transactions = Trades.query.filter_by( user_id=current_user.username).order_by(Trades.trade_date) if transactions.count() == 0: form = TradeForm() form.trade_currency.data = current_app.fx['code'] form.trade_date.data = datetime.utcnow() return render_template("warden/empty_txs.html", title="Empty Transaction List", current_app=current_app, current_user=fx_rate(), form=form) if request.method == "GET": Trades.query.filter_by(user_id=current_user.username).delete() current_app.db.session.commit() regenerate_nav() flash("ALL TRANSACTIONS WERE DELETED", "danger") return redirect(url_for("warden.warden_page")) else: return redirect(url_for("warden.warden_page"))
def allocation_history(): return render_template("warden/allocation_history.html", title="Portfolio Historical Allocation", current_app=current_app, current_user=fx_rate())
def drawdown(): return render_template("warden/drawdown.html", title="Drawdown Analysis", current_app=current_app, current_user=fx_rate())
def running_services(): return render_template("warden/running_services.html", title="Running Services and Status", current_app=current_app, current_user=fx_rate())
def edittransaction(): form = TradeForm() reference_id = request.args.get("reference_id") id = request.args.get("id") if reference_id: trade = Trades.query.filter_by( user_id=current_user.username).filter_by( trade_reference_id=reference_id).first() if trade.count() == 0: abort(404, "Transaction not found") id = trade.id trade = Trades.query.filter_by(user_id=current_user.username).filter_by( id=id).first() if trade is None: abort(404) if trade.user_id != current_user.username: abort(403) acclist = AccountInfo.query.filter_by(user_id=current_user.username) accounts = [] for item in acclist: accounts.append((item.account_longname, item.account_longname)) form.trade_account.choices = accounts form.submit.label.text = 'Edit Trade' if request.method == "POST": if form.validate_on_submit(): # Write changes to database if form.trade_operation.data in ("B", "D"): qop = 1 elif form.trade_operation.data in ("S", "W"): qop = -1 else: qop = 0 # Calculate Trade's cash value cvfail = False try: p = float(clean_float(form.trade_price.data)) q = float(clean_float(form.trade_quantity.data)) f = float(clean_float(form.trade_fees.data)) cv = qop * (q * p) + f except ValueError: flash( "Error on calculating cash amount \ for transaction - TRADE NOT edited. Try Again.", "danger", ) cvfail = True cv = 0 trade.trade_date = form.trade_date.data trade.trade_asset_ticker = form.trade_asset_ticker.data trade.trade_currency = form.trade_currency.data trade.trade_operation = form.trade_operation.data trade.trade_quantity = float(form.trade_quantity.data) * qop trade.trade_price = float(clean_float(form.trade_price.data)) trade.trade_fees = float(clean_float(form.trade_fees.data)) trade.trade_account = form.trade_account.data trade.trade_notes = form.trade_notes.data trade.cash_value = cv if not cvfail: current_app.db.session.commit() regenerate_nav() flash("Trade edit successful", "success") return redirect(url_for("warden.warden_page")) flash("Trade edit failed. Something went wrong. Try Again.", "danger") # Pre-populate the form form.trade_date.data = trade.trade_date form.trade_currency.data = trade.trade_currency form.trade_asset_ticker.data = trade.trade_asset_ticker form.trade_operation.data = trade.trade_operation form.trade_quantity.data = abs(float(trade.trade_quantity)) form.trade_price.data = trade.trade_price form.trade_fees.data = trade.trade_fees form.trade_account.data = trade.trade_account form.trade_notes.data = trade.trade_notes return render_template("warden/edittransaction.html", title="Edit Transaction", form=form, trade=trade, id=id, current_app=current_app, current_user=fx_rate())
def volchart(): return render_template("warden/volchart.html", title="Historical Volatility Chart", current_app=current_app, current_user=fx_rate())
def newtrade(): form = TradeForm() acclist = AccountInfo.query.filter_by(user_id=current_user.username) accounts = [] for item in acclist: accounts.append((item.account_longname, item.account_longname)) form.trade_account.choices = accounts if request.method == "POST": if form.validate_on_submit(): # Need to include two sides of trade: if form.trade_operation.data in ("B"): qop = 1 elif form.trade_operation.data in ("S"): qop = -1 else: qop = 0 flash( "Trade Operation Error. Should be B for buy or S for sell.", "warning") # Calculate Trade's cash value cvfail = False try: p = float(clean_float(form.trade_price.data)) q = float(clean_float(form.trade_quantity.data)) f = float(clean_float(form.trade_fees.data)) cv = qop * (q * p) + f except ValueError: flash( "Error on calculating fiat amount \ for transaction - TRADE NOT included", "danger", ) cvfail = True cv = 0 # Check what type of trade this is # Cash and/or Asset try: tquantity = float(form.trade_quantity.data) * qop except ValueError: tquantity = 0 try: tprice = float(form.trade_price.data) except ValueError: tprice = 0 trade = Trades( user_id=current_user.username, trade_date=form.trade_date.data, trade_account=form.trade_account.data, trade_currency=form.trade_currency.data, trade_asset_ticker=form.trade_asset_ticker.data, trade_quantity=tquantity, trade_operation=form.trade_operation.data, trade_price=tprice, trade_fees=form.trade_fees.data, trade_notes=form.trade_notes.data, cash_value=cv, ) if not cvfail: current_app.db.session.add(trade) current_app.db.session.commit() regenerate_nav() flash("Trade included", "success") return redirect(url_for("warden.warden_page")) else: flash("Trade Input failed. Something went wrong. Try Again.", "danger") form.trade_currency.data = current_app.fx['code'] form.trade_date.data = datetime.utcnow() return render_template("warden/newtrade.html", form=form, title="New Trade", current_app=current_app, current_user=fx_rate())
def transactions_fx(): # Gets the transaction table and fills with fx information # Note that it uses the currency exchange for the date of transaction # Get all transactions from Specter and format # SPECTER ============================================ df = specter_df() if not df.empty: df['trade_date'] = pd.to_datetime(df['trade_date']) df = df.set_index('trade_date') # Ignore times in df to merge - keep only dates df.index = df.index.floor('d') df.index.rename('date', inplace=True) # SQL DATABASE ======================================== # Get all transactions from db and format df_sql = pd.read_sql_table('trades', current_app.db.engine) if not df_sql.empty: df_sql = df_sql[(df_sql.user_id == current_user.username)] # df = df[(df.trade_operation == "B") | (df.trade_operation == "S")] df_sql['trade_date'] = pd.to_datetime(df_sql['trade_date']) df_sql = df_sql.set_index('trade_date') # Ignore times in df to merge - keep only dates df_sql.index = df_sql.index.floor('d') df_sql.index.rename('date', inplace=True) # Merge both df = df.append(df_sql, sort=False) if df.empty: flash(f"No Transactions Found. Running Demo Portfolio.", "info") logging.warning( "Transactions_FX - No txs found - using Demo Portfolio") sample_trade = { 'user_id': current_user.username, 'trade_date': datetime(2018, 1, 1), 'trade_currency': 'USD', 'trade_fees': 0, 'trade_fees_fx': 0, 'trade_quantity': 0.1, 'trade_multiplier': 1, 'trade_price': 14920, 'trade_asset_ticker': 'BTC', 'trade_operation': 'B', 'status': 'Demo Line', 'trade_account': 'Demo Account', 'cash_value': 1492, 'cash_value_fx': 1492, } df = df.append(sample_trade, ignore_index=True) df = df.set_index('trade_date') return df # The current fx needs no conversion, set to 1 df[fx_rate()['fx_rate']] = 1 # Need to get currencies into the df in order to normalize # let's load a list of currencies needed and merge list_of_fx = df.trade_currency.unique().tolist() # loop through currency list for currency in list_of_fx: if currency == fx_rate()['fx_rate']: continue # Make a price request df[currency] = df.apply(find_fx, axis=1) # Now create a cash value in the preferred currency terms df['fx'] = df.apply(lambda x: x[x['trade_currency']], axis=1) df['cash_value_fx'] = df['cash_value'].astype(float) / df['fx'].astype( float) df['trade_fees_fx'] = df['trade_fees'].astype(float) / df['fx'].astype( float) df['trade_price_fx'] = df['trade_price'].astype(float) / df['fx'].astype( float) if 'trade_date' not in df.columns: df['trade_date'] = pd.to_datetime(df.index) return (df)
def portfolio_compare(): return render_template("warden/portfolio_compare.html", title="Portfolio Comparison", current_app=current_app, current_user=fx_rate())
def price_and_position(): # Gets price and position data for a specific ticker ticker = request.args.get("ticker") fx = request.args.get("fx") if fx is None: fx = fx_rate()['base'] # Gets Price and market data first realtime_data = realtime_price(ticker=ticker, fx=fx) historical_data = historical_prices(ticker=ticker, fx=fx) historical_data.index = historical_data.index.astype('datetime64[ns]') filemeta = (ticker + "_" + fx + ".meta") historical_meta = pickle_it(action='load', filename=filemeta) price_chart = historical_data[["close_converted", "close"]].copy() # dates need to be in Epoch time for Highcharts price_chart.index = price_chart.index.astype('datetime64[ns]') price_chart.index = (price_chart.index - datetime(1970, 1, 1)).total_seconds() price_chart.index = price_chart.index * 1000 price_chart.index = price_chart.index.astype(np.int64) price_chart = price_chart.to_dict() price_chart_usd = price_chart["close"] price_chart = price_chart["close_converted"] # Now gets position data df = positions() if isinstance(df, pd.DataFrame): if not df.empty: df = df[df['trade_asset_ticker'] == ticker] df_trades = transactions_fx() position_chart = None if isinstance(df_trades, pd.DataFrame): df_trades = df_trades[df_trades['trade_asset_ticker'] == ticker] if not df_trades.empty: df_trades = df_trades.sort_index(ascending=True) df_trades['trade_quantity_cum'] = df_trades[ 'trade_quantity'].cumsum() position_chart = df_trades[["trade_quantity_cum"]].copy() # dates need to be in Epoch time for Highcharts position_chart.index = position_chart.index.astype( 'datetime64[ns]') position_chart.index = (position_chart.index - datetime(1970, 1, 1)).total_seconds() position_chart.index = position_chart.index * 1000 position_chart.index = position_chart.index.astype(np.int64) position_chart = position_chart.to_dict() position_chart = position_chart["trade_quantity_cum"] if ticker == 'GBTC': from pricing_engine.engine import GBTC_premium from parseNumbers import parseNumber GBTC_price = parseNumber(realtime_data['price']) GBTC_fairvalue, GBTC_premium = GBTC_premium(GBTC_price) else: GBTC_premium = GBTC_fairvalue = None return render_template("warden/price_and_position.html", title="Ticker Price and Positions", current_app=current_app, current_user=fx_rate(), realtime_data=realtime_data, historical_data=historical_data, historical_meta=historical_meta, positions=df, ticker=ticker, fx=fx, price_chart=price_chart, price_chart_usd=price_chart_usd, position_chart=position_chart, GBTC_premium=GBTC_premium, GBTC_fairvalue=GBTC_fairvalue)
def portstats(): meta = {} # Looking to generate the following data here and return as JSON # for AJAX query on front page: # Start date, End Date, Start NAV, End NAV, Returns (1d, 1wk, 1mo, 1yr, # YTD), average daily return. Best day, worse day. Std dev of daily ret, # Higher NAV, Lower NAV + dates. Higher Port Value (date). data = generatenav() meta["start_date"] = (data.index.min()).date().strftime("%B %d, %Y") meta["end_date"] = data.index.max().date().strftime("%B %d, %Y") meta["start_nav"] = data["NAV_fx"][0] meta["end_nav"] = float(data["NAV_fx"][-1]) meta["max_nav"] = float(data["NAV_fx"].max()) meta["max_nav_date"] = data[ data["NAV_fx"] == data["NAV_fx"].max()].index.strftime("%B %d, %Y")[0] meta["min_nav"] = float(data["NAV_fx"].min()) meta["min_nav_date"] = data[ data["NAV_fx"] == data["NAV_fx"].min()].index.strftime("%B %d, %Y")[0] meta["end_portvalue"] = data["PORT_fx_pos"][-1].astype(float) meta["end_portvalue_usd"] = meta["end_portvalue"] / fx_rate()['fx_rate'] meta["max_portvalue"] = data["PORT_fx_pos"].astype(float).max() meta["max_port_date"] = data[data["PORT_fx_pos"] == data["PORT_fx_pos"]. max()].index.strftime("%B %d, %Y")[0] meta["min_portvalue"] = round(data["PORT_fx_pos"].min(), 0) meta["min_port_date"] = data[data["PORT_fx_pos"] == data["PORT_fx_pos"]. min()].index.strftime("%B %d, %Y")[0] meta["return_SI"] = (meta["end_nav"] / meta["start_nav"]) - 1 # Temporary fix for an issue with portfolios that are just too new # Create a function to handle this try: meta["return_1d"] = (meta["end_nav"] / data["NAV_fx"][-2]) - 1 except IndexError: meta["return_1d"] = "-" try: meta["return_1wk"] = (meta["end_nav"] / data["NAV_fx"][-7]) - 1 except IndexError: meta["return_1wk"] = "-" try: meta["return_30d"] = (meta["end_nav"] / data["NAV_fx"][-30]) - 1 except IndexError: meta["return_30d"] = "-" try: meta["return_90d"] = (meta["end_nav"] / data["NAV_fx"][-90]) - 1 except IndexError: meta["return_90d"] = "-" try: meta["return_ATH"] = (meta["end_nav"] / meta["max_nav"]) - 1 except IndexError: meta["return_ATH"] = "-" try: yr_ago = pd.to_datetime(datetime.today() - relativedelta(years=1)) yr_ago_NAV = data.NAV_fx[data.index.get_loc(yr_ago, method="nearest")] meta["return_1yr"] = meta["end_nav"] / yr_ago_NAV - 1 except IndexError: meta["return_1yr"] = "-" # Create data for summa"age meta["fx"] = current_app.settings['PORTFOLIO']['base_fx'] meta["daily"] = {} for days in range(1, 8): meta["daily"][days] = {} meta["daily"][days]["date"] = data.index[days * -1].date().strftime( "%A <br> %m/%d") meta["daily"][days]["nav"] = data["NAV_fx"][days * -1] meta["daily"][days]["nav_prev"] = data["NAV_fx"][(days + 1) * -1] meta["daily"][days]["perc_chg"] = (meta["daily"][days]["nav"] / meta["daily"][days]["nav_prev"]) - 1 meta["daily"][days]["port"] = data["PORT_fx_pos"][days * -1] meta["daily"][days]["port_prev"] = data["PORT_fx_pos"][(days + 1) * -1] meta["daily"][days]["port_chg"] = (meta["daily"][days]["port"] - meta["daily"][days]["port_prev"]) # Removes Numpy type from json - returns int instead def convert(o): if isinstance(o, np.int64): return int(o) else: return (o) # create chart data for a small NAV chart return simplejson.dumps(meta, ignore_nan=True, default=convert)
def show_log(): return render_template('warden/show_log.html', title="Debug Viewer", current_app=current_app, current_user=fx_rate())