def run(self): start_time = datetime.now() PrinterUtils.console_log( message= f"Started trading at {start_time} and will ended at {self.__run_stop_time}" ) delta_minutes = start_time while not TradeBotUtils.is_run_time_passed( current_time=datetime.now(), run_stop_time=self.__run_stop_time): delta_minutes = self.__print_trading_data(delta_minutes) try: if self.__trade_bot.is_account_order_matching_market_order(): order_id = self.__trade_bot.execute_order() if self.__trade_bot.is_order_executed(order_id): self.__trade_bot.run_post_trade_tasks(order_id) self.__switch_trader() except (websockets.exceptions.ConnectionClosedError, websockets.exceptions.ConnectionClosedOK, websockets.exceptions.InvalidStatusCode) as e: self.__reconnect_websocket() PrinterUtils.console_log( message= f"Started trading at {start_time} and ended at {datetime.now()}")
def init_trades_to_database_from_exchange( self, database_service: DatabaseService): PrinterUtils.console_log(message=f"Initializing Database from kraken!") closed_transactions = self.get_transactions() trade_nr = 1 for idx, order_id in enumerate( reversed(list(closed_transactions.keys()))): if self.__is_successful_order(closed_transactions, order_id): database_service.insert_trade_report( order_id=order_id, is_live=True, exchange='kraken', timestamp=self.get_transaction_timestamp( closed_transactions[order_id]), trade_number=trade_nr, buy=self.is_transaction_buy(closed_transactions[order_id]), cash_currency=self.get_transaction_cash_currency( closed_transactions[order_id]), crypto_currency=self.get_transaction_crypto_currency( closed_transactions[order_id]), fee=self.get_transaction_fee_from_transaction_dict( closed_transactions[order_id]), price=self.get_transaction_price_per_quantity( closed_transactions[order_id]), quantity=self.get_transaction_quantity( closed_transactions[order_id]), gross_trade_value=self.get_transaction_gross_value( closed_transactions[order_id]), net_trade_value=self.get_transaction_net_value( closed_transactions[order_id])) trade_nr += 1
def run_queries_from_file(self, file_path): with open(file_path, 'r') as sql_file: queries = sql_file.read().strip().split(';') for query in [f'{query};' for query in queries]: self.write_query(query=query, data=[]) PrinterUtils.console_log(message=f'Query Executed: Run Queries from File')
def __get_transactions(self) -> DataFrame: PrinterUtils.console_log("Fetching Transactions from Database") query = f"SELECT * FROM tax_management.trades" transactions = self._database_service.custom_read_query_to_dataframe( query=query).sort_values(by=['datetime']).reset_index(drop=True) self.__convert_currencies(transactions) return transactions
def _get_initial_bid_price(self, exchange_websocket: ExchangeWebsocket) -> float: if self._configs.is_sell: return 0 is_invalid_account_bid_price = True account_bid_price = 0 while is_invalid_account_bid_price: market_bid_price = exchange_websocket.get_market_bid_price() market_ask_price = exchange_websocket.get_market_ask_price() print( f'\nMarket bid: {market_bid_price} \nMarket ask: {market_ask_price}' ) account_bid_price = float(input('Set start account BID price: ')) is_invalid_account_bid_price = market_ask_price - account_bid_price < 0 if is_invalid_account_bid_price: PrinterUtils.console_log( message= "ERROR: Account bid price has to be smaller than market ask price." ) else: PrinterUtils.console_log( message=f"Account Bid Price: {account_bid_price}") return account_bid_price
def get_accrued_account_fees(self, exchange: str, cash_currency: str, crypto_currency: str, is_live: bool) -> float: accrued_fee = 0 for currency in TradeBotUtils.get_permitted_cash_currencies(): query = "SELECT SUM(fee) from trade_data.report" \ " WHERE live = %s" \ " AND exchange = %s" \ " AND cash_currency = %s" \ " AND crypto_currency = %s;" data = [ is_live, exchange.lower(), currency.lower(), crypto_currency.lower() ] result = self.read_query(query=query, data=data)[0][0] if result: accrued_fee += self._currency_converter.convert_currency_from_api( value=float(result), from_currency=currency, to_currency=cash_currency) PrinterUtils.console_log( message=f'Query Executed: Get Accrued Account Fees') return float(accrued_fee)
def custom_read_query(self, query: str, data: list): result = self.read_query(query, data)[0][0] if isinstance(result, decimal.Decimal): return float(result) if isinstance(result, str): return str(result) PrinterUtils.console_log(message=f'Custom Query Executed') return result
def __reconnect_websocket(self): wait_time_in_seconds = 120 PrinterUtils.console_log( message= f"{datetime.now()}: Attempting to Reconnect Websocket in {wait_time_in_seconds} seconds" ) time.sleep(wait_time_in_seconds) self.__trade_bot.reconnect_websocket()
def __send_email(self, message: MIMEMultipart): server = smtplib.SMTP('smtp.gmail.com', 587) server.starttls() server.login(self.__email_source, self.__email_source_password) text = message.as_string() server.sendmail(self.__email_source, self.__email_target, text) server.quit() PrinterUtils.console_log(message=f'Email Sent: {datetime.now()}')
def tax_calculations(self): all_transactions = self.__get_transactions() k4_values = [] for crypto_currency in all_transactions.crypto_currency.unique(): PrinterUtils.console_log(f"Calculating for {crypto_currency}! --") transactions = all_transactions[all_transactions["crypto_currency"] == crypto_currency].reset_index( drop=True) self.__save_transactions(transactions, crypto_currency) total_overhead_value = 0 avg_overhead_value = 0 total_quantity = -self.reserved_xrp( ) if crypto_currency == "xrp" else 0 total_profit = 0 overhead_value_calculations = [] for idx in transactions.index.values: transaction = transactions.iloc[idx] if transaction['buy']: total_quantity += transaction["quantity"] total_overhead_value += transaction['gross_trade_value'] avg_overhead_value = total_overhead_value / total_quantity profit = 0 else: total_quantity -= transaction["quantity"] total_overhead_value -= avg_overhead_value * transaction[ 'quantity'] profit = transaction['net_trade_value'] - transaction[ 'quantity'] * avg_overhead_value total_profit += profit if transaction["quantity"] >= math.pow(10, -5): k4_values.append([ transaction['datetime'], crypto_currency, transaction["quantity"], transaction['net_trade_value'], transaction["quantity"] * avg_overhead_value ]) overhead_value_calculations.append([ transaction['datetime'], "Köp" if transaction["buy"] else "Sälj", total_quantity, total_overhead_value, avg_overhead_value, "" if profit <= math.pow(10, -5) else profit, "" if profit <= math.pow(10, -5) else profit * self.__tax_percent, "", total_profit, total_profit * self.__tax_percent ]) self.__save_overhead_value_calulations(overhead_value_calculations, crypto_currency) self.__print_current_values(total_quantity, total_overhead_value, avg_overhead_value, total_profit) if k4_values: self.__save_k4(k4_values)
def __log_trading_data(self, is_buy: bool, headers: list, output: list): headers[1] = "Account Trade Price" headers[2] = "Market Trade Price" headers.insert(1, "Is Buy") output.insert(1, is_buy) PrinterUtils.log_data( headers=headers, output=output, file_path=TradeBotUtils.get_generated_file_path( f"{self.__exchange_name.lower()}_trading_data_log.csv"))
def print_trading_data(self, is_buy: bool): if is_buy: account_trade = f"Account Bid Price [{self.__currency_symbols[self.__cash_currency]}]" account_trade_price = trading_cache.account_bid_price market_trade = f"Market Ask Price [{self.__currency_symbols[self.__cash_currency]}]" market_trade_price = trading_cache.market_ask_price current_value_description = "Cash Value" current_value = trading_cache.cash_value trade_quantity = f"Buy Quantity {self.__crypto_currency.upper()}" trade_quantity_value = trading_cache.buy_quantity percent_profit = "Cash Profit [%]" percent_profit_value = ProfitCalculatorUtil.percent_cash_profit( cash_value=current_value, initial_value=trading_cache.initial_value) else: account_trade = f"Account Ask Price [{self.__currency_symbols[self.__cash_currency]}]" account_trade_price = trading_cache.account_ask_price market_trade = f"Market Bid Price [{self.__currency_symbols[self.__cash_currency]}]" market_trade_price = trading_cache.market_bid_price current_value_description = f"Position Net Value [{self.__currency_symbols[self.__cash_currency]}]" current_value = trading_cache.net_position_value trade_quantity = f"Sell Quantity {self.__crypto_currency.upper()}" trade_quantity_value = trading_cache.sell_quantity percent_profit = "Position Profit [%]" percent_profit_value = ProfitCalculatorUtil.percent_cash_profit( cash_value=current_value, initial_value=trading_cache.initial_value) headers = [ 'Timestamp', trade_quantity, account_trade, market_trade, f'Initial Value [{self.__currency_symbols[self.__cash_currency]}]', current_value_description, percent_profit, f'Accrued Fees [{self.__currency_symbols[self.__cash_currency]}]', 'Nr Buy+Sell Cycles' ] output = [ datetime.now(), trade_quantity_value, account_trade_price, market_trade_price, trading_cache.initial_value, current_value, percent_profit_value, trading_cache.accrued_fee, trading_cache.successful_cycles ] PrinterUtils.print_data_as_tabulate(headers=headers, output=output) self.__log_trading_data(is_buy, headers, output)
def get_initial_account_value(self, exchange: str, is_live: bool, cash_currency: str) -> float: query = f"SELECT initial_account_value_{cash_currency.lower()} from trade_data.initial_account_value" \ f" WHERE exchange = %s" \ f" AND live = %s;" data = [exchange, is_live] result = self.read_query(query=query, data=data) PrinterUtils.console_log( message=f'Query Executed: Get initial Account Value') return float(result[0][0]) if result else 0
def drop_schema_tables(self, schema: str): query = "SELECT table_name FROM information_schema.tables" \ " WHERE table_schema = %s" data = [schema] table_names = self.read_query(query, data) table_names = [f"{schema}." + table_name[0] for table_name in table_names] separator = ', ' query = f"DROP TABLE {separator.join(table_names)}" self.write_query(query=query, data=[]) PrinterUtils.console_log(message=f'Query Executed: Drop all Schema Tables')
def _get_exchange_websocket(self) -> ExchangeWebsocket: PrinterUtils.console_log( message= f"Exchange {self._configs.exchange} WebSocket is being used for trading {self._configs.crypto_currency}" f" in {self._configs.cash_currency}") if self._configs.exchange == 'bitstamp': return BitstampWebsocket(self._configs.cash_currency, self._configs.crypto_currency) else: return KrakenWebsocket(self._configs.cash_currency, self._configs.crypto_currency)
def insert_trade_report(self, is_live: bool, exchange: str, trade_number: int, timestamp: datetime, order_id: str, buy: bool, price: float, cash_currency: str, quantity: float, crypto_currency: str, fee: float, gross_trade_value: float, net_trade_value: float): query = f'INSERT INTO tax_management.trades(order_id, live, exchange,' \ ' datetime, account_trade_number, buy, price, cash_currency,' \ ' quantity, crypto_currency, fee, gross_trade_value, net_trade_value) ' \ 'VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ON CONFLICT DO NOTHING;' data = [order_id, is_live, exchange.lower(), timestamp, trade_number, buy, price, cash_currency.lower(), quantity, crypto_currency.lower(), fee, gross_trade_value, net_trade_value] self.write_query(query=query, data=data) PrinterUtils.console_log(message=f'Query Executed: Insert Trade Report')
def get_nr_successful_cycles(self, exchange: str, crypto_currency: str, is_live: bool) -> int: query = "SELECT COUNT(account_trade_number) from trade_data.report" \ " WHERE live = %s " \ " AND buy = %s" \ " AND exchange = %s" \ " AND crypto_currency = %s;" data = [is_live, False, exchange.lower(), crypto_currency] result = self.read_query(query=query, data=data)[0][0] PrinterUtils.console_log( message=f'Query Executed: Get nr of Successful Cycles') return int(result) if result else 0
def __print_current_values(self, total_quantity: float, total_overhead_value: float, avg_overhead_value: float, total_profit: float): headers = [ "Date", "Total Quantity", "Total Overhead Value", "Average Overhead value", "Total Profit", "Total Tax" ] output = [ date.today(), total_quantity, total_overhead_value, avg_overhead_value, total_profit, total_profit * self.__tax_percent ] PrinterUtils.console_log(message=f"Current State --") PrinterUtils.print_data_as_tabulate(headers=headers, output=output)
def __live_run_checker(self): if self.__configs.is_live: contract_pin = random.randint(10000, 99999) signature = int( input( f"\nWARNING LIVE RUN. If you want to trade sign the contract by entering: {contract_pin}: " )) if signature != int(contract_pin): PrinterUtils.console_log(message="ABORTING") exit() else: PrinterUtils.console_log( message="Contract signed! Live run accepted!")
def __save_tax_report_yearly_sales_per_crypto_currency( self, overhead_value_calculations_df: DataFrame, crypto_currency: str): calculated_sell_transactions_df = overhead_value_calculations_df.loc[ overhead_value_calculations_df['Händelse'] == "Sälj"] overhead_value_calculations_df = calculated_sell_transactions_df.loc[ calculated_sell_transactions_df['Datum'].dt.year == self._year] overhead_value_calculations_df.to_csv( TaxManagementUtils.get_generated_file_path( f"overhead_value_calculations_sales_{crypto_currency}_{self._year}.csv" )) PrinterUtils.console_log( message=f"Tax Report {self._year} Saved for {crypto_currency}")
def __convert_currencies(self, transactions: DataFrame): PrinterUtils.console_log(message="Converting values to SEK") columns_to_convert = ['fee', "gross_trade_value", "net_trade_value"] conversion_fee = 0.02 for column in columns_to_convert: transactions[column] = transactions.apply( lambda x: x[column] if x['cash_currency'] == 'sek' else (1 - conversion_fee) * self ._currency_converter.convert_currency_from_api( value=x[column], from_currency=x['cash_currency'], to_currency='sek', date=x['datetime'].strftime("%Y-%m-%d")), axis=1)
def is_order_executed(self, order_id: str) -> bool: time.sleep(5) while self.__is_order_status_open(order_id): PrinterUtils.console_log( message=f"Order id {order_id} is still open") time.sleep(20) if self.__is_order_executed(order_id): return True else: PrinterUtils.console_log( message= f'{datetime.now()} - Order: {order_id} was not executed!') time.sleep(5) return False
def print_and_store_trade_report(self, is_buy: bool, fee: float, order_id: str): if is_buy: value = trading_cache.cash_value quantity = trading_cache.buy_quantity price = trading_cache.market_ask_price else: value = trading_cache.gross_position_value quantity = trading_cache.sell_quantity price = trading_cache.market_bid_price headers = [ 'Timestamp', 'is_live', 'exchanges', 'Trade Number', 'Is Buy', f'Price [{self.__currency_symbols[self.__cash_currency]}]', f'Quantity {self.__crypto_currency.upper()}', f'Gross Trade Value [{self.__currency_symbols[self.__cash_currency]}]', f'Net Trade Value [{self.__currency_symbols[self.__cash_currency]}]', f'Fee [{self.__currency_symbols[self.__cash_currency]}]' ] output = [ datetime.now(), self.__is_live, self.__exchange_name, trading_cache.successful_trades, is_buy, price, quantity, value, value - fee, fee ] PrinterUtils.print_data_as_tabulate(headers, output) PrinterUtils.log_data( headers=headers, output=output, file_path=TradeBotUtils.get_generated_file_path( f"{self.__exchange_name.lower()}_successful_trade_log.csv")) self.__database_service.insert_trade_report( order_id=order_id, is_live=self.__is_live, exchange=self.__exchange_name, trade_number=trading_cache.successful_trades, timestamp=datetime.now(), buy=is_buy, price=price, quantity=quantity, cash_currency=self.__cash_currency, crypto_currency=self.__crypto_currency, gross_trade_value=value, net_trade_value=value - fee, fee=fee)
def get_transactions_as_dataframe(self, exchange: str, is_live: bool, cash_currency: str, crypto_currency: str) -> DataFrame: query = "SELECT buy, cash_currency, net_trade_value, account_trade_number from trade_data.report" \ " WHERE live = {}" \ " AND exchange = '{}'" \ " AND crypto_currency = '{}';" \ .format(is_live, exchange.lower(), crypto_currency.lower()) transaction_df = self.read_to_dataframe(query) transaction_df['net_trade_value'] = transaction_df.apply( lambda x: x['net_trade_value'] if x[ 'cash_currency'] == cash_currency else self._currency_converter .convert_currency_from_api(value=x['net_trade_value'], from_currency=x['cash_currency'], to_currency=cash_currency), axis=1) PrinterUtils.console_log( message=f'Query Executed: Get Transaction as DataFrame') return transaction_df
def __get_exchange_api(self) -> ExchangeApi: PrinterUtils.console_log(message=f"Exchange {self._configs.exchange} Api is being used for trading {self._configs.crypto_currency}" f" in {self._configs.cash_currency} with interest: {self._configs.interest * 100}%") if self._configs.exchange == 'bitstamp': exchange_api = BitstampApi(cash_currency=self._configs.cash_currency, crypto_currency=self._configs.crypto_currency, customer_id=TradeBotUtils.get_bitstamp_customer_id(), api_key=TradeBotUtils.get_bitstamp_api_key(), api_secret=TradeBotUtils.get_bitstamp_api_secret()) else: exchange_api = KrakenApi(cash_currency=self._configs.cash_currency, crypto_currency=self._configs.crypto_currency, api_key=TradeBotUtils.get_kraken_api_key(), api_secret=TradeBotUtils.get_kraken_api_secret()) if self._configs.init_database_from_exchange: exchange_api.init_trades_to_database_from_exchange(database_service=self._database_service) return exchange_api
def insert_or_update_initial_account_value(self, exchange: str, is_live: bool, account_value: float, cash_currency: str): query = "INSERT INTO trade_data.initial_account_value(exchange, live, initial_account_value_usd, initial_account_value_eur)" \ " VALUES(%s, %s, %s, %s)" \ " ON CONFLICT (exchange, live) " \ " DO UPDATE" \ " SET" \ " initial_account_value_usd = excluded.initial_account_value_usd," \ " initial_account_value_eur = excluded.initial_account_value_eur;" data = [ exchange, is_live, self._currency_converter.convert_currency_from_api( account_value, cash_currency, 'usd'), self._currency_converter.convert_currency_from_api( account_value, cash_currency, 'eur') ] self.write_query(query=query, data=data) PrinterUtils.console_log( message=f'Query Executed: Insert initial Account Value')
async def __async__connect(self): PrinterUtils.console_log(message="Attempting connection to {}".format(self.__uri)) self._websocket = await websockets.connect(self.__uri) PrinterUtils.console_log(message="Connected")
def run_post_trade_tasks(self, order_id: str): fee = trading_cache.cash_value * trading_cache.exchange_fee self.update_cache(order_id, fee) self._create_visual_trade_report() self._email_trade_reports() PrinterUtils.console_log(message="Post Trade Task Finished!")
def run_post_trade_tasks(self, order_id: str): fee = self.__exchange_api.get_transaction_fee(order_id) self.update_cache(order_id, fee) self._create_visual_trade_report() self._email_trade_reports() PrinterUtils.console_log(message="Post Trade Task Finished!")