def withdraw(exchange_name, target_exchange_name, amount_str): db = session.get_a_trading_db_mysql_session() try: exchange_data = make_exchange_data_from_key(exchange_name, db) target_exchange_data = make_exchange_data_from_key(target_exchange_name, db) target_exchange = make_exchange_from_key(target_exchange_name) amount = Money.loads(amount_str) addr = target_exchange.current_deposit_address exchange_data.record_withdrawal(target_exchange_data, amount, addr) session.commit_mysql_session(db) logger.info(tc.colored("Recorded %s withdrawal from %s" % (amount, exchange_name), "green")) finally: db.remove()
def get_burn_transactions(db, start_time, end_time): """ Get the list of transactions associated with the 'BURN' account in the databse. If there is no burn account, this function will return None. """ burn_exchange_data = None try: burn_exchange_data = exchange_factory.make_exchange_data_from_key( 'BURN', db) except Exception as e: logger.info(NO_BURN_ACCOUNT_WARNING) return None burn_exchange_id = burn_exchange_data.exchange_id burn_transactions = db.query(Transaction)\ .filter(Transaction.transaction_status == Transaction.COMPLETED)\ .filter(Transaction.time_completed >= start_time)\ .filter(Transaction.time_completed < end_time)\ .filter(Transaction.exchange_id == burn_exchange_id)\ .order_by(Transaction.time_completed.desc())\ .all() return burn_transactions
def withdraw_fiat(exchange_name, target_exchange_name, amount_str, deposit_amount_str, transaction_details): db = session.get_a_trading_db_mysql_session() try: exchange_data = make_exchange_data_from_key(exchange_name, db) target_exchange_data = make_exchange_data_from_key(target_exchange_name, db) amount = Money.loads(amount_str) if deposit_amount_str: deposit_amount = Money.loads(deposit_amount_str) exchange_data.record_fiat_withdrawal(target_exchange_data, amount, deposit_amount=deposit_amount, transaction_details=transaction_details) else: exchange_data.record_fiat_withdrawal(target_exchange_data, amount, transaction_details=transaction_details) session.commit_mysql_session(db) logger.info(tc.colored("Recorded %s withdrawal from %s" % (amount, exchange_name), "green")) finally: db.remove()
def audit_bank_accounts(db, scraper): """ Check the balances of each bank account. If they differ, look for and add the new transactions. Writes BANK_AUDIT_<key> heartbeats on success. """ try: account_data = scraper.load() except Scraper.MaintenanceException as e: # The heartbeats will fail, but the audit will retry every hour. scraper_name = scraper.__class__.__name__ logger.info( tc.colored('%s: Service Unavailable' % scraper_name, 'yellow')) msg = str(e) if msg: logger.info(tc.colored(msg, 'yellow')) return for account in account_data: account_num = account['account_number'] account_key = account_num_to_key(account_num) db_account = exchange_factory.make_exchange_data_from_key( account_key, db) db_balance = db_account.balance.fiat() ledger_balance = db_account.ledger_balance().fiat() if ledger_balance != db_balance: msg = 'DB balance: %s != Ledger balance: %s' % (db_balance, ledger_balance) logger.info(tc.colored(msg, 'red')) return bank_balance = account['balance'] if bank_balance == db_balance: success(account_key) else: balance_diff = bank_balance - db_balance transactions = scraper.load_transactions(account_num) new_transactions = find_new_transactions(transactions, balance_diff) record_transactions(db_account, new_transactions, db) db_balance = db_account.balance.fiat() if bank_balance == db_balance: success(account_key) else: msg = 'Our balance: %s != Bank balance: %s' % (db_balance, bank_balance) logger.info(tc.colored(msg, 'red'))
def manual_btc_withdrawals(db): """ Check the Manual BTC exchanges for being above their target. Ping a dev on hipchat if they are. """ logger.info('Running manual BTC withdrawals') for name, target in MANUAL_BTC_EXCHANGES.iteritems(): exchange_db = exchange_factory.make_exchange_data_from_key(name, db) if exchange_db.balance['BTC'] > target: withdrawal_amount = exchange_db.balance['BTC'] - target if withdrawal_amount > BTC_TRANSFER_UNIT: btc_withdrawal_notification(exchange_db, withdrawal_amount)
def transaction_complete(exchange_name, currency): db = session.get_a_trading_db_mysql_session() try: exchange_data = make_exchange_data_from_key(exchange_name, db) tr = db.query(Transaction).filter_by(exchange=exchange_data).filter_by(_amount_currency=currency).filter_by(transaction_status=Transaction.IN_TRANSIT).order_by(Transaction.time_created).first() if tr: tr.complete() session.commit_mysql_session(db) if tr.transaction_type == Transaction.DEPOSIT: action = "deposit to" elif tr.transaction_type == Transaction.WITHDRAWL: action = "withdrawal from" logger.info(tc.colored("Recorded %s %s %s" % (tr.amount, action, exchange_name), "green")) else: logger.info(tc.colored("No Transaction of that currency found", "red")) finally: db.remove()
def record_reverse_burn(db_account, transaction, db): """ Record transactions which "unexpectedly" put money back into our bank account. We expect this for interest, Matinee payments, reversed bank errors, etc. """ if transaction['type'] != Transaction.DEPOSIT: raise ValueError('Reverse burn must be a deposit') amount = transaction['amount'] transaction_details = {} transaction_details['description'] = transaction['description'] burn_account = exchange_factory.make_exchange_data_from_key('BURN', db) deposit, __ = burn_account.record_fiat_withdrawal( db_account, amount, transaction_details=transaction_details, ) deposit.complete()
def record_burn(db_account, transaction, db): """ Record transactions which "unexpectedly" took money out of our bank account. We expect this for burn: payroll, rent, bills, etc. """ if transaction['type'] != Transaction.WITHDRAWL: raise ValueError('Burn must be a withdrawal') amount = transaction['amount'] transaction_details = {} transaction_details['description'] = transaction['description'] burn_account = exchange_factory.make_exchange_data_from_key('BURN', db) deposit, __ = db_account.record_fiat_withdrawal( burn_account, amount, transaction_details=transaction_details, ) deposit.complete()
def get_trading_bank_accounts(self): trading_bank_acount_keys = ['BMO_USD', 'BMO_CAD'] bank_account_infos = [] for key in trading_bank_acount_keys: account = exchange_factory.make_exchange_data_from_key( key, self.trading_db, ) account_info = { 'name': account.name, 'balance': account.balance.fiat(), 'highlight': account.balance.fiat() > BANK_ACCOUNT_HIGHLIGHT_THRESHOLD, } bank_account_infos.append(account_info) return bank_account_infos
def ledger_balance(db, start_time=None, end_time=None, include_pending=False, exchange_names=[]): """ Returns the exchange's balance as calculated from its ledger of trades and transactions. With no params specified we return the current balance. With end_time specified we return the balance at a historical point. With start_time specified we return a balance diff which can be useful for performance (if you know the balance on one day, you can get the next day's balance by only looking at that next days ledgers instead of from the beginning). """ if start_time and start_time < EARLIEST_CORRECT_LEDGER_DATE: raise ValueError( 'start_time must be later than %s' % EARLIEST_CORRECT_LEDGER_DATE, ) if end_time and end_time < EARLIEST_CORRECT_LEDGER_DATE: raise ValueError( 'end_time must be later than %s' % EARLIEST_CORRECT_LEDGER_DATE, ) exchange_ids = [] for exchange_name in exchange_names: exchange_data = exchange_factory.make_exchange_data_from_key( exchange_name, db) exchange_id = exchange_data.exchange_id exchange_ids.append(exchange_id) # Trades bid_prices = trade_position_query( db, Trade._price, Trade._price_currency, Trade.BID, start_time=start_time, end_time=end_time, exchange_names=exchange_names, ) bid_volumes = trade_position_query( db, Trade._volume, Trade._volume_currency, Trade.BID, start_time=start_time, end_time=end_time, exchange_names=exchange_names, ) ask_prices = trade_position_query( db, Trade._price, Trade._price_currency, Trade.ASK, start_time=start_time, end_time=end_time, exchange_names=exchange_names, ) ask_volumes = trade_position_query( db, Trade._volume, Trade._volume_currency, Trade.ASK, start_time=start_time, end_time=end_time, exchange_names=exchange_names, ) trade_fees = trade_position_query( db, Trade._fee, Trade._fee_currency, start_time=start_time, end_time=end_time, exchange_names=exchange_names, ) # Transactions deposits = transaction_position_query( db, Transaction._amount, Transaction._amount_currency, transaction_type=Transaction.DEPOSIT, start_time=start_time, end_time=end_time, include_pending=include_pending, exchange_ids=exchange_ids, ) withdrawals = transaction_position_query( db, Transaction._amount, Transaction._amount_currency, transaction_type=Transaction.WITHDRAWL, start_time=start_time, end_time=end_time, include_pending=include_pending, exchange_ids=exchange_ids, ) transaction_fees = transaction_position_query( db, Transaction._fee, Transaction._fee_currency, start_time=start_time, end_time=end_time, include_pending=include_pending, exchange_ids=exchange_ids, ) logger.debug('Bid Prices: %s' % bid_prices) logger.debug('Bid Volumes: %s' % bid_volumes) logger.debug('Ask Prices: %s' % ask_prices) logger.debug('Ask Volumes: %s' % ask_volumes) logger.debug('Trade Fees: %s' % trade_fees) logger.debug('Deposits: %s' % deposits) logger.debug('Withdrawals: %s' % withdrawals) logger.debug('Transaction Fees: %s' % transaction_fees) positive_position = sum([ bid_volumes, ask_prices, deposits, ], Position()) negative_position = sum([ bid_prices, ask_volumes, trade_fees, withdrawals, transaction_fees, ], Position()) total_position = positive_position - negative_position return total_position
def get_ledger_table_for_time(self, exchange_name, start_time, end_time, currency=None): if currency: currency = currency.upper() exchange_name = exchange_name.capitalize() exchange = exchange_factory.make_exchange_from_key(exchange_name) exchange_data = exchange_factory.make_exchange_data_from_key( exchange_name, self.trading_db, ) if exchange: # Normal bitcoin exchange. fiat_currency = exchange.currency else: # Bank Accounts don't have exchange objects. fiat_currency = self.currency_for_bank_account(exchange_name) # Bank Accounts only have one currency, so we set a currency filter by # default. It just makes the UI look nicer instead of having an empty BTC # column. currency = fiat_currency ledger = exchange_data.ledger( start_time=start_time, end_time=end_time, currency=currency, ) # ledger is empty for this time period. if len(ledger) == 0: return fiat_currency, currency, [] # get the exchange balance from before these ledger entries started oldest_entry = ledger[-1] oldest_time = oldest_entry.time_created # There can be multiple entries at the same time entries_at_oldest_time = [] for entry in ledger: if isinstance(entry, Trade) and entry.time_created == oldest_time: entries_at_oldest_time.append(entry) elif isinstance( entry, Transaction) and entry.time_completed == oldest_time: entries_at_oldest_time.append(entry) oldest_time = pytz.utc.localize(oldest_time) balance_at_oldest_time = exchange_data.ledger_balance( end_time=oldest_time) # balance_at_oldest_time includes all the entries_at_oldest_time # so we need to remove them to get the pre-ledger balance pre_ledger_balance = balance_at_oldest_time for entry in entries_at_oldest_time: pre_ledger_balance -= entry.position starting_ledger_balance = exchange_data.ledger_balance( end_time=start_time) ledger_table = [] for entry in ledger: if isinstance(entry, Trade): table_entries = self.table_entries_from_trade(entry) elif isinstance(entry, Transaction): table_entries = self.table_entries_from_transaction(entry) ledger_table += table_entries ledger_table = self.filter_to_currency(ledger_table, currency) current_balance = starting_ledger_balance for table_entry in reversed(ledger_table): if 'credit' in table_entry: current_balance += table_entry['credit'] if 'debit' in table_entry: current_balance -= table_entry['debit'] table_entry['balance'] = current_balance return fiat_currency, currency, ledger_table
def get_ledger_table_for_time(exchange_name, start_time, end_time, currency=None): if currency: currency = currency.upper() exchange_name = exchange_name.capitalize() exchange = exchange_factory.make_exchange_from_key(exchange_name) exchange_data = exchange_factory.make_exchange_data_from_key( exchange_name, db, ) fiat_currency = exchange.currency ledger = exchange_data.ledger( start_time=start_time, end_time=end_time, currency=currency, ) # ledger is empty for this time period. if len(ledger) == 0: return fiat_currency, currency, [] # get the exchange balance from before these ledger entries started oldest_entry = ledger[-1] oldest_time = oldest_entry.time_created # There can be multiple entries at the same time entries_at_oldest_time = [] for entry in ledger: if isinstance(entry, Trade) and entry.time_created == oldest_time: entries_at_oldest_time.append(entry) elif isinstance(entry, Transaction) and entry.time_completed == oldest_time: entries_at_oldest_time.append(entry) oldest_time = pytz.utc.localize(oldest_time) balance_at_oldest_time = exchange_data.ledger_balance(end_time=oldest_time) # balance_at_oldest_time includes all the entries_at_oldest_time # so we need to remove them to get the pre-ledger balance pre_ledger_balance = balance_at_oldest_time for entry in entries_at_oldest_time: pre_ledger_balance -= entry.position starting_ledger_balance = exchange_data.ledger_balance(end_time=start_time) ledger_diff = exchange_data.ledger_balance( start_time=start_time, end_time=end_time, ) ending_ledger_balance = starting_ledger_balance + ledger_diff ledger_table = [] for entry in ledger: if isinstance(entry, Trade): table_entries = table_entries_from_trade(entry) elif isinstance(entry, Transaction): table_entries = table_entries_from_transaction(entry) ledger_table += table_entries #ledger_table = self.filter_to_currency(ledger_table, currency) current_balance = starting_ledger_balance for table_entry in reversed(ledger_table): if 'credit' in table_entry: current_balance += table_entry['credit'] if 'debit' in table_entry: current_balance -= table_entry['debit'] table_entry['balance'] = current_balance return fiat_currency, currency, ledger_table