def newtrade(): form = NewTrade() 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.cash_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", "D"): qop = 1 elif form.trade_operation.data in ("S", "W"): qop = -1 else: qop = 0 flash("Trade Operation Error. Should be B, S, D or W.", "warning") # Create a unique ID random_hex = secrets.token_hex(21) # Calculate Trade's cash value cvfail = False if form.trade_type.data != "3": try: p = float(cleancsv(form.trade_price.data)) q = float(cleancsv(form.trade_quantity.data)) f = float(cleancsv(form.trade_fees.data)) cv = qop * (q * p) + f except ValueError: flash( "Error on calculating cash amount \ for transaction - TRADE NOT included", "danger", ) cvfail = True cv = 0 # Check what type of trade this is # Cash and/or Asset if form.trade_type.data == "1" or form.trade_type.data == "2": 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_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, trade_reference_id=random_hex, cash_value=cv, ) if not cvfail: db.session.add(trade) db.session.commit() if form.trade_type.data == "1": # First side is done, now for the matching side-financial only if form.trade_operation.data == "D": acc = "W" elif form.trade_operation.data == "W": acc = "D" elif form.trade_operation.data == "B": acc = "W" elif form.trade_operation.data == "S": acc = "D" else: acc = "" trade = Trades( user_id=current_user.username, trade_date=form.trade_date.data, trade_account=form.cash_account.data, trade_asset_ticker="USD", trade_operation=acc, trade_price="1", trade_quantity=float(cleancsv(form.cash_value.data)), trade_fees=0, cash_value=cv * (-1), trade_notes=f"Matching Trade for trade id: \ <{random_hex} > - included as a pair", trade_reference_id=random_hex, ) db.session.add(trade) db.session.commit() if form.trade_type.data == "3": # Cash Only Transaction try: cv = qop * (float(cleancsv(form.cash_value.data))) except ValueError: flash("Error on calculating cash amount for transaction", "warning") cv = 0 cvfail = True trade = Trades( user_id=current_user.username, trade_date=form.trade_date.data, trade_account=form.cash_account.data, trade_asset_ticker="USD", trade_quantity=cv, trade_price=1, trade_operation=form.trade_operation.data, trade_fees=form.trade_fees.data, trade_notes=form.trade_notes.data, cash_value=cv, trade_reference_id=random_hex, ) if not cvfail: db.session.add(trade) db.session.commit() # re-generates the NAV on the background - delete First # the local NAV file so it's not used. usernamehash = hashlib.sha256( current_user.username.encode("utf-8")).hexdigest() filename = "thewarden/nav_data/" + usernamehash + ".nav" logging.info(f"[newtrade] {filename} marked for deletion.") # Since this function can be run as a thread, # it's safer to delete the current NAV file if it exists. # This avoids other tasks reading the local file which # is outdated try: os.remove(filename) logging.info("[newtrade] Local NAV file deleted") except OSError: logging.info("[newtrade] Local NAV file not found" + " for removal - continuing") generatenav_thread = threading.Thread( target=generatenav, args=(current_user.username, True)) logging.info("Change to database - generate NAV") generatenav_thread.start() if not cvfail: flash("Trade included", "success") return redirect(url_for("main.home")) else: flash("Trade Input failed. Something went wrong. Try Again.", "danger") form.trade_date.data = datetime.utcnow() return render_template("newtrade.html", form=form, title="New Trade")
def importcsv(): form = ImportCSV() if request.method == "POST": if form.validate_on_submit(): if form.submit.data: if form.csvfile.data: test_file = "thewarden/dailydata/test.csv" filename = os.path.join(current_path(), test_file) os.makedirs(os.path.dirname(filename), exist_ok=True) filename = "thewarden/dailydata/" + form.csvfile.data.filename filename = os.path.join(current_path(), filename) form.csvfile.data.save(filename) csv_reader = open(filename, "r", encoding="utf-8") csv_reader = csv.DictReader(csv_reader) csvfile = form.csvfile.data return render_template( "importcsv.html", title="Import CSV File", form=form, csv=csv_reader, csvfile=csvfile, filename=filename, ) if request.method == "GET": filename = request.args.get("f") if filename: csv_reader = open(filename, "r", encoding="utf-8") # csv_reader = csv.DictReader(csv_reader) errors = 0 errorlist = [] a = 0 # skip first line where field names are accounts = AccountInfo.query.filter_by( user_id=current_user.username).order_by( AccountInfo.account_longname) for line in csv_reader: if a != 0: items = line.split(",") random_hex = secrets.token_hex(21) # Check if there is any data on this line: empty = True for l in range(0, 10): try: if items[l] != "": empty = False except IndexError: pass if empty: continue # import TimeStamp Field try: tradedate = parser.parse(items[0]) except ValueError: tradedate = datetime.now() errors = errors + 1 errorlist.append(f"missing date on line: {a}") # Check the Operation Type try: if "B" in items[2]: qop = 1 operation = "B" elif "S" in items[2]: qop = -1 operation = "S" elif "D" in items[2]: qop = 1 operation = "D" elif "W" in items[2]: qop = -1 operation = "W" else: qop = 0 operation = "X" errors = errors + 1 errorlist.append(f"missing operation on line {a}") except IndexError: qop = 0 operation = "X" errors = errors + 1 errorlist.append(f"missing operation on line {a}") # Import Quantity try: if items[4].replace(" ", "") != "": quant = abs(cleancsv(items[4])) * qop else: quant = 0 except ValueError: quant = 0 errors = errors + 1 errorlist.append( f"Quantity error on line {a} - quantity \ {items[4]} could not be converted") # Import Price try: if items[5].replace(" ", "") != "": price = cleancsv(items[5]) else: price = 0 except ValueError: price = 0 errors = errors + 1 errorlist.append(f"Price error on line {a} - price \ {items[5]} could not be converted") # Import Fees try: if items[6].replace(" ", "").replace("\n", "") != "": fees = cleancsv(items[6]) else: fees = 0 except ValueError: fees = 0 errors = errors + 1 errorlist.append( f"error #{errors}: Fee error on line {a} - Fee --\ {items[6]}-- could not be converted") # Import Notes try: notes = items[8] except IndexError: notes = "" # Import Account try: account = items[1] except ValueError: account = "" errors = errors + 1 errorlist.append(f"Missing account on line {a}") # Import Asset Symbol try: ticker = items[3].replace(" ", "") if ticker != "USD": listcrypto = listofcrypto.query.filter_by( symbol=ticker) if listcrypto is None: errors = errors + 1 errorlist.append( f"ticker {ticker} in line {a} \ imported but not found in pricing list") except ValueError: ticker = "" errors = errors + 1 errorlist.append(f"Missing ticker on line {a}") # Find Trade Reference, if none, assign one try: tradeid = items[9] except (ValueError, IndexError): random_hex = secrets.token_hex(21) tradeid = random_hex # Import Cash Value - if none, calculate try: if items[7].replace(" ", "").replace("\n", "") != "": cashvalue = cleancsv(items[7]) else: cashvalue = ((price) * (quant)) + fees except ValueError: cashvalue = 0 errors = errors + 1 errorlist.append( f"error #{errors}: Cash_Value error on line \ {a} - Cash_Value --{items[7]}-- could not \ be converted") trade = Trades( user_id=current_user.username, trade_date=tradedate, trade_account=account, trade_asset_ticker=ticker, trade_quantity=quant, trade_operation=operation, trade_price=price, trade_fees=fees, trade_notes=notes, cash_value=qop * cashvalue, trade_reference_id=tradeid, ) db.session.add(trade) db.session.commit() regenerate_nav() # Check if current account is in list, if not, include curacc = accounts.filter_by( account_longname=account).first() if not curacc: account = AccountInfo(user_id=current_user.username, account_longname=account) db.session.add(account) db.session.commit() a = a + 1 # re-generates the NAV on the background # re-generates the NAV on the background - delete First # the local NAV file so it's not used. usernamehash = hashlib.sha256( current_user.username.encode("utf-8")).hexdigest() filename = "thewarden/nav_data/" + usernamehash + ".nav" filename = os.path.join(current_path(), filename) logging.info(f"[newtrade] {filename} marked for deletion.") # Since this function can be run as a thread, # it's safer to delete the current NAV file if it exists. # This avoids other tasks reading the local file which # is outdated try: os.remove(filename) logging.info("[importcsv] Local NAV file deleted") except OSError: logging.info("[importcsv] Local NAV file not found" + " for removal - continuing") generatenav_thread = threading.Thread(target=generatenav, args=(current_user.username, True)) logging.info("[importcsv] Change to database - generate NAV") generatenav_thread.start() if errors == 0: flash("CSV Import successful", "success") if errors > 0: logging.error("Errors found. Total of ", errors) flash( "CSV Import done but with errors\ - CHECK TRANSACTION LIST", "danger", ) for error in errorlist: flash(error, "warning") return redirect(url_for("main.home")) return render_template("importcsv.html", title="Import CSV File", form=form)
def newtrade(): form = NewTrade() 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.cash_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", "D"): qop = 1 elif form.trade_operation.data in ("S", "W"): qop = -1 else: qop = 0 flash("Trade Operation Error. Should be B, S, D or W.", "warning") # Create a unique ID for this transaction random_hex = secrets.token_hex(21) # Calculate Trade's cash value cvfail = False if form.trade_type.data != "3": try: p = float(cleancsv(form.trade_price.data)) q = float(cleancsv(form.trade_quantity.data)) f = float(cleancsv(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 if form.trade_type.data == "1" or form.trade_type.data == "2": 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, trade_reference_id=random_hex, cash_value=cv, ) if not cvfail: db.session.add(trade) db.session.commit() regenerate_nav() if form.trade_type.data == "1": # First side is done, now for the matching side-financial only if form.trade_operation.data == "D": acc = "W" elif form.trade_operation.data == "W": acc = "D" elif form.trade_operation.data == "B": acc = "W" elif form.trade_operation.data == "S": acc = "D" else: acc = "" trade = Trades( user_id=current_user.username, trade_date=form.trade_date.data, trade_account=form.cash_account.data, trade_currency=form.trade_currency.data, trade_asset_ticker=form.trade_currency.data, trade_operation=acc, trade_price="1", trade_quantity=float(cleancsv(form.cash_value.data)), trade_fees=0, cash_value=cv * (-1), trade_notes=f"Matching Trade for trade id: \ <{random_hex} > - included as a pair", trade_reference_id=random_hex, ) db.session.add(trade) db.session.commit() regenerate_nav() if form.trade_type.data == "3": # Cash Only Transaction try: cv = qop * (float(cleancsv(form.cash_value.data))) except ValueError: flash("Error on calculating cash amount for transaction", "warning") cv = 0 cvfail = True trade = Trades( user_id=current_user.username, trade_date=form.trade_date.data, trade_account=form.cash_account.data, trade_currency=form.trade_currency.data, trade_asset_ticker=form.trade_currency.data, trade_quantity=cv, trade_price=1, trade_operation=form.trade_operation.data, trade_fees=form.trade_fees.data, trade_notes=form.trade_notes.data, cash_value=cv, trade_reference_id=random_hex, ) if not cvfail: db.session.add(trade) db.session.commit() regenerate_nav() if not cvfail: flash("Trade included", "success") return redirect(url_for("main.home")) else: flash("Trade Input failed. Something went wrong. Try Again.", "danger") form.trade_currency.data = current_user.fx() form.trade_date.data = datetime.utcnow() return render_template("newtrade.html", form=form, title="New Trade")