Esempio n. 1
0
    def __init__(self, verbose: bool = False):
        """Constructor.

        Args:
            verbose (bool, optional): [description]. Defaults to False."""
        Logger.verbose_console_log(
            verbose=arguments.verbose,
            message="Stocker is initializing in verbose mode",
            message_type=Message.MESSAGE_TYPE.STATUS)

        self.verbose = verbose
        self.ciphers: dict = initialize_lock_and_key_ciphers()
        self.keys: dict = load_json_resource(
            file_name_cipher=self.ciphers["file_name"],
            data_cipher=self.ciphers['data'],
            resource_file_name=Stocker.KEY_FILE_NAME)
        self.passes: dict = load_json_resource(
            file_name_cipher=self.ciphers["file_name"],
            data_cipher=self.ciphers['data'],
            resource_file_name=Stocker.PASS_FILE_NAME)[Stocker.USER_NAME]
        deposit_history: DataFrame = self.load_csv_resource(
            resource_file_name=Stocker.DEPOSIT_HISTORY_FILE_NAME)
        self.binance_account = BinanceAccount(
            deposit_history=deposit_history[deposit_history.Service ==
                                            "binance"],
            transaction_history=self.load_csv_resource(
                resource_file_name=Stocker.
                BINANCE_TRANSACTION_HISTORY_FILE_NAME),
            api_key=self.keys['binance.us']['API Key'],
            api_secret=self.keys['binance.us']['Secret Key'])
        self.coinbase_account = CoinbaseAccount(
            deposit_history=deposit_history[deposit_history.Service ==
                                            "coinbase"],
            transaction_history=self.load_csv_resource(
                resource_file_name=Stocker.
                COINBASE_TRANSACTION_HISTORY_FILE_NAME),
            api_key=self.keys['coinbase']['API Key'],
            api_secret=self.keys['coinbase']['API Secret'])
        self.mint = Mint(email=self.passes['mint']['email'],
                         password=self.passes['mint']['password'])
        self.holdings: Holdings = Holdings.load(
            holding_file_name=Stocker.HOLDINGS_FILE_NAME,
            file_name_cipher=self.ciphers['file_name'],
            data_cipher=self.ciphers['data'],
            mint=self.mint)

        # Start price update thread
        self.price_checker_thread: PriceChecker = PriceChecker(
            stocker=self, verbose=self.verbose)
        self.price_checker_thread.start()
        self.holdings.initial_update.wait(timeout_ms=60000)

        self.generated_windows = {}
Esempio n. 2
0
 def connection_test(self) -> bool:
     try:
         test_connection = self.create_connection()
         Logger.console_log(message="Connection test with " + self.database + " located at " + self.host
                                    + " was a success",
                            status=Message.MESSAGE_TYPE.SUCCESS)
         return True
     except mariadb.Error as err:
         Logger.console_log(message="Unable to establish connection with database " + self.database + ". Error ["
                                    + str(err) + "] was returned",
                            status=Message.MESSAGE_TYPE.FAIL)
         return False
Esempio n. 3
0
    def run(self) -> None:
        self.running = True
        Logger.verbose_console_log(verbose=self.verbose,
                                   message=str(type(self)) + " is running...",
                                   message_type=Message.MESSAGE_TYPE.STATUS)

        while self.running:
            epoch_start_time = time()
            self.stocker.holdings.update(binance_account = self.stocker.binance_account, 
                                         coinbase_account = self.stocker.coinbase_account, 
                                         verbose = self.stocker.verbose)
            pseudo_realtime_timestep(epoch_start_time=epoch_start_time,
                                     timestep=1/self.rate)
Esempio n. 4
0
    def calculate_equity(self,
                         holding_type: Union[str,
                                             Holdings.HOLDING_TYPE] = "all",
                         verbose: bool = False) -> float:
        """[summary]

        Args:
            holding_type (Union[str, Holdings.HOLDING_TYPE], optional): [description]. Defaults to "all".

        Returns:
            float: [description]"""
        assert type(holding_type) == str or type(
            holding_type) == Holdings.HOLDING_TYPE

        Logger.verbose_console_log(
            verbose=verbose,
            message=str(type(self)) +
            " is calculating the equity of holding_type: " + str(holding_type),
            message_type=Message.MESSAGE_TYPE.STATUS)

        equity: float = 0.

        if type(holding_type) == str:
            holding_type: Holdings.HOLDING_TYPE = Holdings.HOLDING_TYPE(
                holding_type)

        if holding_type == Holdings.HOLDING_TYPE.STOCK or holding_type == Holdings.HOLDING_TYPE.ALL:
            equity += Holdings.calculate_holding_equity(holdings=self.stocks,
                                                        verbose=verbose)
        if holding_type == Holdings.HOLDING_TYPE.CRYPTOCURRENCY or holding_type == Holdings.HOLDING_TYPE.ALL:
            equity += Holdings.calculate_holding_equity(
                holdings=self.cryptocoins, verbose=verbose)
        if holding_type == Holdings.HOLDING_TYPE.CHECKING or holding_type == Holdings.HOLDING_TYPE.ALL:
            equity += Holdings.calculate_holding_equity(
                holdings=self.checking_accounts, verbose=verbose)
        if (holding_type == Holdings.HOLDING_TYPE.FLOATING_USD
                or holding_type == Holdings.HOLDING_TYPE.ALL
            ) and self.floating_usd is not None:
            equity += Holdings.calculate_holding_equity(
                holdings=self.floating_usd, verbose=verbose)

        Logger.verbose_console_log(
            verbose=verbose,
            message=str(type(self)) + " equity of holding_type: " +
            str(holding_type) + " = " + "$%.2f" % equity,
            message_type=Message.MESSAGE_TYPE.SUCCESS)

        return equity
Esempio n. 5
0
    def open_window(self, window_class: Callable[[], QMainWindow]) -> None:
        """Opens a window class and displays it.

        Args:
            window_class (Callable[[], QMainWindow]): [description]"""
        Logger.verbose_console_log(verbose=self.verbose,
                                   message=str(type(self)) +
                                   " is opening window: " + str(window_class),
                                   message_type=Message.MESSAGE_TYPE.STATUS)

        window = window_class(stocker=self)

        if window_class in self.generated_windows.keys():
            self.generated_windows[window_class].append(window)
        else:
            self.generated_windows[window_class] = [window]

        window.show()
Esempio n. 6
0
def get_stock_price(symbol: str) -> float:
    """[summary]

    Args:
        symbol (str): [description]

    Returns:
        DataFrame: [description]"""
    try:
        price: float = get_live_price(symbol)
        return price
    except Exception as error:
        Logger.console_log(
            message=
            "Exception {} encountered when attempting to get price for symbol {}. Sleeping and trying again."
            .format(error, symbol),
            message_type=Message.MESSAGE_TYPE.MINOR_FAIL)
        sleep(120)
        return get_stock_price(symbol=symbol)
Esempio n. 7
0
    def calculate_holding_equity(holdings: Union[
        Dict[str, Holdings.Stock], Dict[str, Holdings.Cryptocoin],
        Dict[str, Holdings.CheckingAccount], Dict[str, float]],
                                 verbose: bool = False) -> float:
        """[summary]

        Args:
            holdings (Union[Dict[str, Holdings.Stock], 
                            Dict[str, Holdings.Cryptocoin], 
                            Dict[str, Holdings.CheckingAccount], 
                            Dict[str, Holdings.FloatingUSD]]): [description]
            verbose (bool, optional): [description]. Defaults to False.

        Returns:
            float: [description]"""
        total_equity: float = 0.
        if len(holdings.keys()) > 0:
            equity_type: Callable = type(holdings[list(holdings.keys())[0]])
        else:
            return 0.

        for holding_name, holding in holdings.items():
            if equity_type == float or equity_type == int:
                holding_equity = holding
            else:
                holding_equity = holding.calculate_equity()
            total_equity += holding_equity

            Logger.verbose_console_log(
                verbose=verbose,
                message=str(holding_name) + " has a " + str(equity_type) +
                " value of " + "$%.2f" % holding_equity,
                message_type=Message.MESSAGE_TYPE.STATUS)

        Logger.verbose_console_log(
            verbose=verbose,
            message=str(Holdings) + " has calculated a total " +
            str(equity_type) + " equity of " + "$%.2f" % total_equity,
            message_type=Message.MESSAGE_TYPE.SUCCESS)

        return total_equity
Esempio n. 8
0
    def __init__(self,
                 verbose: bool = True,
                 log: bool = False,
                 overwrite_log: bool = False) -> None:
        Thread.__init__(self)
        self.available_cpus = cpu_count() - 1
        self.logger = Logger(name=DataCollector.SERVER_NAME,
                             file_log=log,
                             verbose=verbose,
                             overwrite=overwrite_log)
        self.ciphers: Dict[str,
                           VigenereCipher] = initialize_lock_and_key_ciphers()
        self.passes: dict = load_json_resource(
            file_name_cipher=self.ciphers["file_name"],
            data_cipher=self.ciphers['data'],
            resource_file_name=DataCollector.PASS_FILE_NAME)[
                DataCollector.USER_NAME]
        self.mint = Mint(email=self.passes['mint']['email'],
                         password=self.passes['mint']['password'])
        self.mode: DataCollector.SERVER_MODE = DataCollector.SERVER_MODE.INVALID

        self.threads = {}
        # Initialize Threads
        # Build worker threads for available number of cpus
        self.threads[DataCollector.WorkerThread] = []
        for available_cpu_index in range(self.available_cpus):
            self.threads[DataCollector.WorkerThread].append(
                DataCollector.WorkerThread(thread_id=available_cpu_index,
                                           scraper_server=self))

        # Build Executive Thread
        self.threads[
            DataCollector.ExecutiveThread] = DataCollector.ExecutiveThread(
                thread_id=0,
                scraper_server=self,
                worker_threads=self.threads[DataCollector.WorkerThread])
Esempio n. 9
0
def get_crypto_price(
        coin_name: str,
        binance_account: Union[None, BinanceAccount] = None,
        coinbase_account: Union[None, CoinbaseAccount] = None) -> float:
    """[summary]

    Args:
        coin_name (str): [description]
        binance_account (Union[None, BinanceAccount], optional): [description]. Defaults to None.
        coinbase_account (Union[None, CoinbaseAccount], optional): [description]. Defaults to None.

    Returns:
        float: [description]"""
    try:
        return get_price(coin_name, "USD")[coin_name]["USD"]
    except Exception as error:
        Logger.console_log(
            message=
            f"Exception {error} found when attempting to get price from cryptocompare.",
            message_type=Message.MESSAGE_TYPE.MINOR_FAIL)

    if binance_account is not None:
        try:
            return float(
                binance_account.interface.get_ticker(symbol=coin_name +
                                                     "USD")['lastPrice'])
        except Exception as error:
            Logger.console_log(
                message=
                f"Exception {error} found when attempting to get price from binance.",
                message_type=Message.MESSAGE_TYPE.MINOR_FAIL)

    if coinbase_account is not None:
        try:
            return float(
                coinbase_account.interface.get_buy_price(
                    currency_pair=coin_name + "-USD")['amount'])
        except Exception as error:
            Logger.console_log(
                message=
                f"Exception {error} found when attempting to get price from coinbase.",
                message_type=Message.MESSAGE_TYPE.MINOR_FAIL)

    sleep(5 * 60)
    return get_crypto_price(coin_name=coin_name,
                            binance_account=binance_account,
                            coinbase_account=coinbase_account)
Esempio n. 10
0
class DataCollector(Thread):
    """Scraper Server object"""
    SERVER_NAME: str = "DataCollector"
    HOLDINGS_FILE_NAME: str = "holdings.json"
    PASS_FILE_NAME: str = "pass.json"
    USER_NAME: str = "jakeadelic"

    def __init__(self,
                 verbose: bool = True,
                 log: bool = False,
                 overwrite_log: bool = False) -> None:
        Thread.__init__(self)
        self.available_cpus = cpu_count() - 1
        self.logger = Logger(name=DataCollector.SERVER_NAME,
                             file_log=log,
                             verbose=verbose,
                             overwrite=overwrite_log)
        self.ciphers: Dict[str,
                           VigenereCipher] = initialize_lock_and_key_ciphers()
        self.passes: dict = load_json_resource(
            file_name_cipher=self.ciphers["file_name"],
            data_cipher=self.ciphers['data'],
            resource_file_name=DataCollector.PASS_FILE_NAME)[
                DataCollector.USER_NAME]
        self.mint = Mint(email=self.passes['mint']['email'],
                         password=self.passes['mint']['password'])
        self.mode: DataCollector.SERVER_MODE = DataCollector.SERVER_MODE.INVALID

        self.threads = {}
        # Initialize Threads
        # Build worker threads for available number of cpus
        self.threads[DataCollector.WorkerThread] = []
        for available_cpu_index in range(self.available_cpus):
            self.threads[DataCollector.WorkerThread].append(
                DataCollector.WorkerThread(thread_id=available_cpu_index,
                                           scraper_server=self))

        # Build Executive Thread
        self.threads[
            DataCollector.ExecutiveThread] = DataCollector.ExecutiveThread(
                thread_id=0,
                scraper_server=self,
                worker_threads=self.threads[DataCollector.WorkerThread])

    def run(self) -> None:
        """
        """
        self.logger.start()
        self.logger.log(message="Starting " + DataCollector.SERVER_NAME,
                        message_type=Message.MESSAGE_TYPE.STATUS)

        self.threads[DataCollector.ExecutiveThread].start()

        # Start Worker Threads
        for worker_thread in self.threads[DataCollector.WorkerThread]:
            worker_thread.start()

        # Wait for Worker Threads to Complete
        for worker_thread in self.threads[DataCollector.WorkerThread]:
            worker_thread.join()

        # Wait for Executive Thread to Complete
        self.threads[DataCollector.ExecutiveThread].join()
        """
        try:
            while mode != DataCollector.SERVER_MODE.SHUTTING_DOWN:
                mode: DataCollector.SERVER_MODE = DataCollector.SERVER_MODE.set_mode()                

                if mode == DataCollector.SERVER_MODE.STOCK_MARKET_OPEN:
                    pass
                elif mode == DataCollector.SERVER_MODE.STOCK_MARKET_CLOSED:
                    pass

                self.logger.log(message=f"{DataCollector.SERVER_NAME} is running in mode {mode}.", 
                                message_type=Message.MESSAGE_TYPE.STATUS)

                sleep(1)
        except (KeyboardInterrupt, SystemExit) as error:
            self.logger.log(message=f"{DataCollector.SERVER_NAME} is shutting down due to exception: {error}", 
                            message_type=Message.MESSAGE_TYPE.STATUS)
        """

        self.logger.stop()

    def load_holdings(self, holding_file_name: str) -> Holdings:
        """[summary]

        Args:
            holding_file_name (str): [description]

        Returns:
            Holdings: [description]"""
        stocks: Dict[str, Holdings.Stock] = {}
        crypto: Dict[str, Holdings.Cryptocoin] = {}
        checking_accounts: Dict[str, Holdings.CheckingAccount] = {}
        floating_usd: Dict[str, float] = {}

        stock_and_crypto_holdings: Dict[str, Dict[str, Dict[
            str, Any]]] = self.load_json_resource(
                resource_file_name=holding_file_name)

        # Load stocks
        for stock_symbol, stock_data in stock_and_crypto_holdings[
                'stock'].items():
            stocks[stock_symbol] = Holdings.Stock(
                symbol=stock_symbol,
                name=stock_data['name'],
                quantity=stock_data['quantity'],
                cost_basis_per_share=stock_data['cost basis per share'])
        # Load crypto
        for coin, coin_data in stock_and_crypto_holdings['crypto'].items():
            crypto[coin] = Holdings.Cryptocoin(
                name=coin_data["name"],
                coin=coin,
                quantity=coin_data['quantity'],
                investment=coin_data['investment'])

        # Load float
        for float_location, usd_in_float in stock_and_crypto_holdings[
                'float'].items():
            floating_usd[float_location] = usd_in_float

        # Load checking accounts from mint
        for account_data in self.mint.get_accounts(False):
            checking_accounts[
                account_data['accountName']] = Holdings.CheckingAccount(
                    name=account_data['accountName'],
                    equity=account_data['value'])
        self.mint.close()

        return Holdings(stocker=self,
                        stocks=stocks,
                        cryptocoins=crypto,
                        checking_accounts=checking_accounts,
                        floating_usd=floating_usd)

    class ExecutiveThread(Thread):
        """[summary]"""
        def __init__(self, thread_id: int, data_collector: DataCollector,
                     worker_threads: list):
            """[summary]

            Args:
                thread_id (int): [description]
                scraper_server (object): [description]
                worker_threads (list): [description]"""
            Thread.__init__(self)
            self.thread_id = thread_id
            self.data_collector = data_collector
            self.worker_threads = worker_threads

        def __str__(self) -> str:
            """
            """
            return str(type(self)) + "_" + str(self.thread_id)

        def run(self) -> None:
            """TODO"""
            pass

    class WorkerThread(Thread):
        """[summary]"""
        def __init__(self, thread_id: int, data_collector: DataCollector):
            """
            Constructor.
            :param thread_id:
            :param scraper_server:
            """
            Thread.__init__(self)
            self.thread_id = thread_id
            self.data_collector: DataCollector = data_collector
            self.holdings_to_update: List[Holdings.Holding] = []

        def __str__(self) -> str:
            """
            """
            return str(type(self)) + "_" + str(self.thread_id)

        def run(self) -> None:
            """TODO"""
            pass

    class SERVER_MODE(Enum):
        INVALID = "invalid"
        STOCK_MARKET_OPEN = "stock market open"
        STOCK_MARKET_CLOSED = "stock market closed"
        SHUTTING_DOWN = "shutting down"

        @staticmethod
        def set_mode() -> DataCollector.SERVER_MODE:
            """[summary]

            Returns:
                DataCollector.SERVER_MODE: [description]"""
            right_now = datetime.now()
            right_now_hour = int(right_now.hour)
            right_now_minute = int(right_now.minute)

            minutes = right_now_hour * 60 + right_now_minute

            # If we are between 9:30 am and 4 pm the stock market is open
            if 960 > minutes > 570 and is_datetime_weekday(
                    current_time=datetime.now()):
                return DataCollector.SERVER_MODE.STOCK_MARKET_OPEN
            else:
                return DataCollector.SERVER_MODE.STOCK_MARKET_CLOSED
Esempio n. 11
0
    def update(self,
               binance_account: Union[BinanceAccount, None] = None,
               coinbase_account: Union[CoinbaseAccount, None] = None,
               verbose: bool = False) -> None:
        """[summary]"""
        update_time = time()

        self.lock.acquire()
        for coin_name, coin in self.cryptocoins.items():
            coin.price = get_crypto_price(coin_name=coin_name,
                                          binance_account=binance_account,
                                          coinbase_account=coinbase_account)
            Logger.verbose_console_log(
                verbose=verbose,
                message=
                f"[CRYPTO] {coin_name} is currently valued at ${coin.price} per coin.",
                message_type=Message.MESSAGE_TYPE.STATUS)
            self.crypto_df = self.crypto_df.append(
                coin.to_series(update_time=update_time))
            self.crypto_df = self.crypto_df.sort_index()

        for stock_symbol, stock in self.stocks.items():
            stock.price = get_stock_price(symbol=stock_symbol)
            Logger.verbose_console_log(
                verbose=verbose,
                message=
                f"[STOCK] {stock.name} is currently valued at ${stock.price} per share.",
                message_type=Message.MESSAGE_TYPE.STATUS)
            self.stocks_df = self.stocks_df.append(
                stock.to_series(update_time=update_time))
            self.stocks_df = self.stocks_df.sort_index()

        for account_name, checking_account in self.checking_accounts.items():
            #checking_account.update()
            Logger.verbose_console_log(
                verbose=verbose,
                message="[CHECKING] " + account_name +
                " is currently valued at $" + str(checking_account.equity),
                message_type=Message.MESSAGE_TYPE.STATUS)
            self.checking_account_df = self.checking_account_df.append(
                checking_account.to_series(update_time=update_time))
            self.checking_account_df = self.checking_account_df.sort_index()

        for location, usd_in_float in self.floating_usd.items():
            self.floating_usd_df = self.floating_usd_df.append(
                Series(
                    {
                        "datetime": update_time,
                        "location": location,
                        "equity": usd_in_float
                    },
                    name=time()))
            self.floating_usd_df = self.floating_usd_df.sort_index()

        self.lock.release()

        Logger.verbose_console_log(verbose=verbose,
                                   message=str(type(self)) +
                                   " is finished updating.",
                                   message_type=Message.MESSAGE_TYPE.SUCCESS)

        if not self.initial_update.is_set():
            self.initial_update.set()
Esempio n. 12
0
 def stop(self) -> None:
     self.running = False
     Logger.verbose_console_log(verbose=self.verbose,
                                message=str(type(self)) + " is stopping...",
                                message_type=Message.MESSAGE_TYPE.STATUS)