diff --git a/fidelityAPI.py b/fidelityAPI.py index 7606e218..09d9514c 100644 --- a/fidelityAPI.py +++ b/fidelityAPI.py @@ -21,6 +21,7 @@ check_if_page_loaded, getDriver, killSeleniumDriver, + maskString, printAndDiscord, printHoldings, stockOrder, @@ -169,13 +170,10 @@ def fidelity_account_info(driver: webdriver) -> dict or None: ) ) account_numbers = javascript_get_classname(driver, "acct-selector__acct-num") - print(f"Accounts: {account_numbers}") # Get account balances via javascript account_values = javascript_get_classname(driver, "acct-selector__acct-balance") - print(f"Values: {account_values}") # Get account names via javascript account_types = javascript_get_classname(driver, "acct-selector__acct-name") - print(f"Account Names: {account_types}") # Make sure all lists are the same length if not ( len(account_numbers) == len(account_values) @@ -242,11 +240,10 @@ def fidelity_holdings(fidelity_o: Brokerage, loop=None): r"(?<=\s{2})[a-zA-Z]{3,4}(?=\s{2})", stocks_list[i] ) stocks_list = stocks_list[0] - print(f"Stocks: {stocks_list}") - holdings_info = javascript_get_classname( - driver, "ag-center-cols-container" - ) - print(f"Holdings Info: {holdings_info}") + # holdings_info = javascript_get_classname( + # driver, "ag-center-cols-container" + # ) + # print(f"Holdings Info: {holdings_info}") for stock in stocks_list: fidelity_o.set_holdings(key, account, stock, "N/A", "N/A") except Exception as e: @@ -318,7 +315,8 @@ def fidelity_transaction(fidelity_o: Brokerage, orderObj: stockOrder, loop=None) accounts_dropdown_in = test.find_elements( by=By.CSS_SELECTOR, value="li" ) - account_label = accounts_dropdown_in[x].text + account_number = fidelity_o.get_account_numbers(key)[x] + account_label = maskString(account_number) accounts_dropdown_in[x].click() sleep(1) # Type in ticker @@ -520,13 +518,13 @@ def fidelity_transaction(fidelity_o: Brokerage, orderObj: stockOrder, loop=None) "arguments[0].click();", error_dismiss ) printAndDiscord( - f"{key} {account_label}: {orderObj.get_action()} {orderObj.get_amount()} shares of {s}. DID NOT COMPLETE! \nEither this account does not have enough shares, or an order is already pending.", + f"{key} account {account_label}: {orderObj.get_action()} {orderObj.get_amount()} shares of {s}. DID NOT COMPLETE! \nEither this account does not have enough shares, or an order is already pending.", loop, ) # Send confirmation else: printAndDiscord( - f"DRY: {key} {account_label}: {orderObj.get_action()} {orderObj.get_amount()} shares of {s}", + f"DRY: {key} account {account_label}: {orderObj.get_action()} {orderObj.get_amount()} shares of {s}", loop, ) sleep(3) diff --git a/helperAPI.py b/helperAPI.py index b9f099ce..727b3041 100644 --- a/helperAPI.py +++ b/helperAPI.py @@ -450,6 +450,15 @@ async def processQueue(): task_queue.task_done() +def maskString(string): + # Mask string (12345678 -> xxxx5678) + string = str(string) + if len(string) < 4: + return string + masked = "x" * (len(string) - 4) + string[-4:] + return masked + + def printHoldings(brokerObj: Brokerage, loop=None): # Helper function for holdings formatting printAndDiscord( @@ -458,7 +467,7 @@ def printHoldings(brokerObj: Brokerage, loop=None): ) for key in brokerObj.get_account_numbers(): for account in brokerObj.get_account_numbers(key): - printAndDiscord(f"{key} ({account}):", loop) + printAndDiscord(f"{key} ({maskString(account)}):", loop) holdings = brokerObj.get_holdings(key, account) if holdings == {}: printAndDiscord("No holdings in Account\n", loop) diff --git a/robinhoodAPI.py b/robinhoodAPI.py index b1dc3277..199c85f2 100644 --- a/robinhoodAPI.py +++ b/robinhoodAPI.py @@ -8,7 +8,7 @@ import robin_stocks.robinhood as rh from dotenv import load_dotenv -from helperAPI import Brokerage, printAndDiscord, printHoldings, stockOrder +from helperAPI import Brokerage, maskString, printAndDiscord, printHoldings, stockOrder def robinhood_init(ROBINHOOD_EXTERNAL=None): @@ -52,7 +52,7 @@ def robinhood_init(ROBINHOOD_EXTERNAL=None): ) atype = rh.account.load_account_profile(info="type", account_number=an) rh_obj.set_account_type(name, an, atype) - print(f"Found {atype} account {an}") + print(f"Found {atype} account {maskString(an)}") # Check for IRA accounts if (len(account) > 3) and (account[3] != "NA"): iras = [account[3]] @@ -61,6 +61,7 @@ def robinhood_init(ROBINHOOD_EXTERNAL=None): for ira in iras: # Make sure it's different from the normal account number if ira == an: + ira = maskString(ira) print( f"ERROR: IRA account {ira} is the same as margin account. Please remove {an} from your .env file." ) @@ -69,9 +70,9 @@ def robinhood_init(ROBINHOOD_EXTERNAL=None): info="account_number", account_number=ira ) if ira_num is None: - print(f"Unable to lookup IRA account {ira}") + print(f"Unable to lookup IRA account {maskString(ira)}") continue - print(f"Found IRA account {ira_num}") + print(f"Found IRA account {maskString(ira_num)}") rh_obj.set_account_number(name, ira_num) rh_obj.set_account_totals( name, @@ -136,6 +137,7 @@ def robinhood_transaction(rho: Brokerage, orderObj: stockOrder, loop=None): ) for account in rho.get_account_numbers(key): obj: rh = rho.get_logged_in_objects(key) + print_account = maskString(account) if not orderObj.get_dry(): try: # Market order @@ -149,7 +151,7 @@ def robinhood_transaction(rho: Brokerage, orderObj: stockOrder, loop=None): # Limit order fallback if market_order is None: printAndDiscord( - f"{key}: Error {orderObj.get_action()}ing {orderObj.get_amount()} of {s} in {account}, trying Limit Order", + f"{key}: Error {orderObj.get_action()}ing {orderObj.get_amount()} of {s} in {print_account}, trying Limit Order", loop, ) ask = obj.get_latest_price(s, priceType="ask_price")[0] @@ -186,7 +188,7 @@ def robinhood_transaction(rho: Brokerage, orderObj: stockOrder, loop=None): ) if limit_order is None: printAndDiscord( - f"{key}: Error {orderObj.get_action()}ing {orderObj.get_amount()} of {s} in {account}", + f"{key}: Error {orderObj.get_action()}ing {orderObj.get_amount()} of {s} in {print_account}", loop, ) continue @@ -194,7 +196,7 @@ def robinhood_transaction(rho: Brokerage, orderObj: stockOrder, loop=None): if limit_order.get("non_field_errors") is not None: message = limit_order["non_field_errors"] printAndDiscord( - f"{key}: {orderObj.get_action()} {orderObj.get_amount()} of {s} in {account} @ {price}: {message}", + f"{key}: {orderObj.get_action()} {orderObj.get_amount()} of {s} in {print_account} @ {price}: {message}", loop, ) else: @@ -202,7 +204,7 @@ def robinhood_transaction(rho: Brokerage, orderObj: stockOrder, loop=None): if market_order.get("non_field_errors") is not None: message = market_order["non_field_errors"] printAndDiscord( - f"{key}: {orderObj.get_action()} {orderObj.get_amount()} of {s} in {account}: {message}", + f"{key}: {orderObj.get_action()} {orderObj.get_amount()} of {s} in {print_account}: {message}", loop, ) except Exception as e: @@ -210,6 +212,6 @@ def robinhood_transaction(rho: Brokerage, orderObj: stockOrder, loop=None): printAndDiscord(f"{key} Error submitting order: {e}", loop) else: printAndDiscord( - f"{key} {account} Running in DRY mode. Transaction would've been: {orderObj.get_action()} {orderObj.get_amount()} of {s}", + f"{key} {print_account} Running in DRY mode. Transaction would've been: {orderObj.get_action()} {orderObj.get_amount()} of {s}", loop, ) diff --git a/schwabAPI.py b/schwabAPI.py index e562b509..e0d4e969 100644 --- a/schwabAPI.py +++ b/schwabAPI.py @@ -8,7 +8,7 @@ from dotenv import load_dotenv from schwab_api import Schwab -from helperAPI import Brokerage, printAndDiscord, printHoldings, stockOrder +from helperAPI import Brokerage, maskString, printAndDiscord, printHoldings, stockOrder def schwab_init(SCHWAB_EXTERNAL=None): @@ -39,7 +39,8 @@ def schwab_init(SCHWAB_EXTERNAL=None): ) account_info = schwab.get_account_info_v2() account_list = list(account_info.keys()) - print(f"The following Schwab accounts were found: {account_list}") + print_accounts = [maskString(a) for a in account_list] + print(f"The following Schwab accounts were found: {print_accounts}") print("Logged in to Schwab!") schwab_obj.set_logged_in_object(name, schwab) for account in account_list: @@ -48,8 +49,8 @@ def schwab_init(SCHWAB_EXTERNAL=None): name, account, account_info[account]["account_value"] ) except Exception as e: - print(traceback.format_exc()) print(f"Error logging in to Schwab: {e}") + print(traceback.format_exc()) return None return schwab_obj @@ -76,6 +77,7 @@ def schwab_holdings(schwab_o: Brokerage, loop=None): schwab_o.set_holdings(key, account, sym, qty, current_price) except Exception as e: printAndDiscord(f"{key} {account}: Error getting holdings: {e}", loop) + print(traceback.format_exc()) printHoldings(schwab_o, loop) @@ -94,6 +96,7 @@ def schwab_transaction(schwab_o: Brokerage, orderObj: stockOrder, loop=None): ) obj: Schwab = schwab_o.get_logged_in_objects(key) for account in schwab_o.get_account_numbers(key): + print_account = maskString(account) # If DRY is True, don't actually make the transaction if orderObj.get_dry(): printAndDiscord( @@ -108,7 +111,7 @@ def schwab_transaction(schwab_o: Brokerage, orderObj: stockOrder, loop=None): dry_run=orderObj.get_dry(), ) printAndDiscord( - f"{key} account {account}: The order verification was " + f"{key} account {print_account}: The order verification was " + "successful" if success else "unsuccessful", @@ -116,11 +119,12 @@ def schwab_transaction(schwab_o: Brokerage, orderObj: stockOrder, loop=None): ) if not success: printAndDiscord( - f"{key} account {account}: The order verification produced the following messages: {messages}", + f"{key} account {print_account}: The order verification produced the following messages: {messages}", loop, ) except Exception as e: printAndDiscord( - f"{key} {account}: Error submitting order: {e}", loop + f"{key} {print_account}: Error submitting order: {e}", loop ) + print(traceback.format_exc()) sleep(1) diff --git a/tastyAPI.py b/tastyAPI.py index ab6a67bf..afcd4187 100644 --- a/tastyAPI.py +++ b/tastyAPI.py @@ -21,7 +21,7 @@ from tastytrade.streamer import DataStreamer from tastytrade.utils import TastytradeError -from helperAPI import Brokerage, printAndDiscord, printHoldings, stockOrder +from helperAPI import Brokerage, maskString, printAndDiscord, printHoldings, stockOrder def order_setup(tt: ProductionSession, order_type, stock_price, stock, amount): @@ -121,6 +121,7 @@ async def tastytrade_execute(tt_o: Brokerage, orderObj: stockOrder, loop=None): loop=loop, ) for i, acct in enumerate(tt_o.get_account_numbers(key)): + print_account = maskString(acct) try: acct: Account = accounts[i] # Set order type @@ -141,7 +142,7 @@ async def tastytrade_execute(tt_o: Brokerage, orderObj: stockOrder, loop=None): order_status = placed_order.order.status.value # Check order status if order_status in ["Received", "Routed"]: - message = f"{key} {acct.account_number}: {orderObj.get_action()} {orderObj.get_amount()} of {s} Order: {placed_order.order.id} Status: {order_status}" + message = f"{key} {print_account}: {orderObj.get_action()} {orderObj.get_amount()} of {s} Order: {placed_order.order.id} Status: {order_status}" if orderObj.get_dry(): message = f"{key} Running in DRY mode. Transaction would've been: {orderObj.get_action()} {orderObj.get_amount()} of {s}" printAndDiscord(message, loop=loop) @@ -151,7 +152,7 @@ async def tastytrade_execute(tt_o: Brokerage, orderObj: stockOrder, loop=None): stock_limit = await streamer.oneshot(EventType.PROFILE, [s]) stock_quote = await streamer.oneshot(EventType.QUOTE, [s]) printAndDiscord( - f"{key} {acct.account_number} Error: {order_status} Trying Limit order...", + f"{key} {print_account} Error: {order_status} Trying Limit order...", loop=loop, ) # Get limit price @@ -181,20 +182,18 @@ async def tastytrade_execute(tt_o: Brokerage, orderObj: stockOrder, loop=None): ) # Check order status if order_status in ["Received", "Routed"]: - message = f"{key} {acct.account_number}: {orderObj.get_action()} {orderObj.get_amount()} of {s} Order: {placed_order.order.id} Status: {order_status}" + message = f"{key} {print_account}: {orderObj.get_action()} {orderObj.get_amount()} of {s} Order: {placed_order.order.id} Status: {order_status}" if orderObj.get_dry(): message = f"{key} Running in DRY mode. Transaction would've been: {orderObj.get_action()} {orderObj.get_amount()} of {s}" printAndDiscord(message, loop=loop) elif order_status == "Rejected": # Only want this message if it fails both orders. printAndDiscord( - f"{key} Error placing order: {placed_order.order.id} on account {acct.account_number}: {order_status}", + f"{key} Error placing order: {placed_order.order.id} on account {print_account}: {order_status}", loop=loop, ) except TastytradeError as te: - printAndDiscord( - f"{key} {acct.account_number}: Error: {te}", loop=loop - ) + printAndDiscord(f"{key} {print_account}: Error: {te}", loop=loop) def tastytrade_transaction(tt: Brokerage, orderObj: stockOrder, loop=None): diff --git a/tradierAPI.py b/tradierAPI.py index fa6d5ead..65c64850 100644 --- a/tradierAPI.py +++ b/tradierAPI.py @@ -9,7 +9,7 @@ import requests from dotenv import load_dotenv -from helperAPI import Brokerage, printAndDiscord, printHoldings, stockOrder +from helperAPI import Brokerage, maskString, printAndDiscord, printHoldings, stockOrder def make_request( @@ -85,7 +85,7 @@ def tradier_init(TRADIER_EXTERNAL=None): an = json_response["profile"]["account"]["account_number"] else: an = json_response["profile"]["account"][x]["account_number"] - print(an) + print(maskString(an)) tradier_obj.set_account_number(name, an) tradier_obj.set_account_type( name, an, json_response["profile"]["account"][x]["type"] @@ -175,43 +175,52 @@ def tradier_transaction(tradier_o: Brokerage, orderObj: stockOrder, loop=None): ) for account in tradier_o.get_account_numbers(key): obj: str = tradier_o.get_logged_in_objects(key) + print_account = maskString(account) # Tradier doesn't support fractional shares if not orderObj.get_amount().is_integer(): printAndDiscord( - f"Tradier account {account} Error: Fractional share {orderObj.get_amount()} not supported", + f"Tradier account {print_account} Error: Fractional share {orderObj.get_amount()} not supported", loop=loop, ) continue if not orderObj.get_dry(): - data = { - "class": "equity", - "symbol": s, - "side": orderObj.get_action(), - "quantity": orderObj.get_amount(), - "type": "market", - "duration": "day", - } - json_response = make_request( - f"accounts/{account}/orders", obj, data=data, method="POST" - ) - if json_response is None: + try: + data = { + "class": "equity", + "symbol": s, + "side": orderObj.get_action(), + "quantity": orderObj.get_amount(), + "type": "market", + "duration": "day", + } + json_response = make_request( + f"accounts/{account}/orders", obj, data=data, method="POST" + ) + if json_response is None: + printAndDiscord( + f"Tradier account {print_account} Error: JSON response is None", + loop=loop, + ) + continue + if json_response.get("order").get("status") is not None: + printAndDiscord( + f"Tradier account {print_account}: {orderObj.get_action()} {orderObj.get_amount()} of {s}: {json_response['order']['status']}", + loop=loop, + ) + continue printAndDiscord( - f"Tradier account {account} Error: JSON response is None", + f"Tradier account {print_account} Error: This order did not route. JSON response: {json.dumps(json_response, indent=2)}", loop=loop, ) - continue - if json_response.get("order").get("status") is not None: + except Exception as e: printAndDiscord( - f"Tradier account {account}: {orderObj.get_action()} {orderObj.get_amount()} of {s}: {json_response['order']['status']}", - loop=loop, + f"Tradier account {print_account} Error: {e}", loop=loop ) + print(traceback.format_exc()) + print(f"JSON response: {json.dumps(json_response, indent=2)}") continue - printAndDiscord( - f"Tradier account {account} Error: This order did not route. JSON response: {json.dumps(json_response, indent=2)}", - loop=loop, - ) else: printAndDiscord( - f"Tradier account {account}: Running in DRY mode. Trasaction would've been: {orderObj.get_action()} {orderObj.get_amount()} of {s}", + f"Tradier account {print_account}: Running in DRY mode. Trasaction would've been: {orderObj.get_action()} {orderObj.get_amount()} of {s}", loop=loop, )