def main(script_arguments, execute): exchange_name = script_arguments['exchange'].upper() start_str = script_arguments['start'] end_str = script_arguments['end'] start_time = parse(start_str).datetime end_time = parse(end_str).datetime export_ledger_for_exchange(exchange_name, start_time, end_time)
def test_creation_with_times(self): l = Liability( Money('100', 'ETH'), Liability.FIXED_INTEREST, 'John', time_started=parse('2016-10-1').datetime, time_repayed=parse('2016-11-1').datetime, ) l.time_started.should.equal(parse('2016-10-1').datetime) l.time_repayed.should.equal(parse('2016-11-1').datetime) l.amount.should.equal(Money('100', 'ETH')) l.entity_name.should.equal('John')
def _get_recent_trades_resp(self, req, return_pagination=False): response, headers = self.resp(req) trades = [] for trade in response: if not trade['settled']: continue price = Money(trade['price'], self.currency) size = Money(trade['size'], 'BTC') fiat = price * size.amount our_type = self._order_mode_to_const(trade['side']) # Strange bug here, delorean isn't parsing the trailing Z on the created_at # date correctly. trade_dict = { 'time': int(parse(trade['created_at'][:-1]).epoch), 'trade_id': str(trade['trade_id']), 'order_id': str(trade['order_id']), 'btc': size, 'fiat': fiat, 'fee': Money(trade['fee'], self.currency), 'type': our_type, } trades.append(trade_dict) if return_pagination: return trades, self.pagination_cursors(headers) else: return trades
def get_transactions_from_page(self, account): """ Parses out records from a transactions page. Assumes the driver has already navigated to a transactions page. """ logger.debug('get_transactions_from_page') account_num = account['account_number'] account_currency = account['balance'].currency self.wait_for_text_on_page('Transaction History') self.wait_for_text_on_page('Balance Forward') # We need to look for the specific account number because the wait_for_text checks above # can match on the transactions page we were previously on logger.debug('waiting for account number to match %s' % account_num) WebDriverWait(self.driver, 10).until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.bodyCopy .cardNumber'), account_num)) transaction_selector = '#ccChequingTransactionTable tbody tr' transaction_els = self.driver.find_elements_by_css_selector(transaction_selector) transaction_els.pop(0) # drop the Balance Forward row transactions = [] for el in transaction_els: date = el.find_element_by_css_selector('td:nth-child(1)').text code = el.find_element_by_css_selector('td:nth-child(2)').text description = el.find_element_by_css_selector('td:nth-child(3)').text raw_debit = el.find_element_by_css_selector('td:nth-child(4)').text raw_credit = el.find_element_by_css_selector('td:nth-child(5)').text raw_balance = el.find_element_by_css_selector('td:nth-child(6)').text if raw_debit and raw_credit: raise Exception('Only one of debit and credit fields should be present') elif not raw_debit and not raw_credit: raise Exception('One of debit and credit fields should be present') elif raw_debit: debit = self.string_to_money(raw_debit + ' ' + account_currency) amount = debit transaction_type = Transaction.WITHDRAWL elif raw_credit: credit = self.string_to_money(raw_credit + ' ' + account_currency) amount = credit transaction_type = Transaction.DEPOSIT balance = self.string_to_money(raw_balance + ' ' + account_currency) timestamp = int(parse(date).epoch) transactions.append({ 'timestamp': timestamp, 'description': description, 'amount': amount, 'type': transaction_type, # 'code': code, # 'balance': balance, }) return transactions
def get_multi_order_details_resp(self, req, order_ids): order_ids = [unicode(o) for o in order_ids] multi_trades = self.trades_for_orders_resp(req, order_ids) data = {} for order_id in order_ids: total_price_currency = Money('0', self.currency) total_volume_currency = Money('0', self.volume_currency) our_trades = [] our_type = None price_currency_key = self.currency.lower() vol_currency_key = self.volume_currency.lower() if order_id in multi_trades: trades = multi_trades[order_id] for t in trades: volume_currency_amount = abs( Money(t[vol_currency_key], self.volume_currency), ) fee = abs(Money(t['fee'], self.currency)) price_currency_amount = abs( Money(t[price_currency_key], self.currency), ) total_price_currency += price_currency_amount total_volume_currency += volume_currency_amount if Decimal(t[vol_currency_key]) > 0: our_type = Consts.BID else: our_type = Consts.ASK our_trades.append({ 'time': int(parse(t['datetime']).epoch), 'trade_id': unicode(t['id']), 'fee': fee, vol_currency_key: volume_currency_amount, 'fiat': price_currency_amount, }) try: time_created = min([t['time'] for t in our_trades]) except ValueError: # This is raised if there are no trades. time_created = None data[order_id] = { 'time_created': time_created, 'type': our_type, '%s_total' % vol_currency_key: total_volume_currency, 'fiat_total': total_price_currency, 'trades': our_trades, } return data
def get_multi_order_details_resp(self, req, order_ids): # This is modeled after Bitstamp, where we get the order details from the # trades endpoint directly. The caveat is that order_details will only work # for the most recent 50 trades. Since we are always accounting trades right # after they happen, this should be ok (and also affects Bitstamp). order_ids = [str(o) for o in order_ids] multi_trades = self.trades_for_orders_resp(req, order_ids) data = {} for order_id in order_ids: total_usd = Money('0', 'USD') total_btc = Money('0', 'BTC') our_trades = [] our_type = None if order_id in multi_trades: trades = multi_trades[order_id] for t in trades: assert (t['currency1'] == 'XBT') btc_amount = Money(t['currency1Amount'], 'BTC') assert (t['currency2'] == 'USD') usd_amount = Money(t['currency2Amount'], 'USD') # This might also come back as XBT, but since ItBit has 0-fee # trading right now, I can't tell. assert (t['commissionCurrency'] == 'USD') fee = Money(t['commissionPaid'], 'USD') total_usd += usd_amount total_btc += btc_amount our_type = self._order_mode_to_const(t['direction']) our_trades.append({ 'time': parse(t['timestamp']).epoch, 'trade_id': None, 'fee': fee, 'btc': btc_amount, 'fiat': usd_amount, }) time_created = None if our_trades: time_created = min([t['time'] for t in our_trades]) data[order_id] = { 'time_created': time_created, 'type': our_type, 'btc_total': total_btc, 'fiat_total': total_usd, 'trades': our_trades } return data
def generate_data(self): gds_active = self.is_gds_connection_active() if gds_active is False: return self.generate_null_data() # Get query string parameters. query_string_high = self.get_argument('high', None) query_string_low = self.get_argument('low', None) show_orders = self.get_argument('show_orders', False) != False at_time = self.get_argument('at_time', None) no_lookback_limit = self.get_argument('no_lookback_limit', None) selected_exchanges = self.get_active_exchanges(self.trading_pair_names) # Some simple formatting of our query string parameters. self.no_lookback_limit = True if no_lookback_limit else False if at_time: at_time = parse(at_time).datetime # Get the data. open_orders_for_exchange = {} open_orders_for_exchange = self.get_open_orders_by_exchange(at_time) levels_for_exchange, low_bid, high_ask = self.get_orderbook_levels( self.gds_db, self.trading_pairs, at_time, selected_exchanges, ) levels_for_exchange = self.format_levels_for_graphing( levels_for_exchange) high, low = self.determine_graph_default_range( query_string_high, query_string_low, low_bid, high_ask, open_orders_for_exchange, ) args = { 'gds_active': gds_active, 'levels_for_exchange': levels_for_exchange, 'selected_exchanges': selected_exchanges, 'open_orders_for_exchange': open_orders_for_exchange, 'high': high, 'low': low, 'pair_name': self.pair_name, 'show_orders': show_orders, 'at_time': None if not at_time else at_time.isoformat(), } return args
def test_creation_with_details(self): l = Liability( Money('100', 'ETH'), Liability.FIXED_INTEREST, 'John', time_started=parse('2016-10-1').datetime, details={'interest_rate': 0.05}, ) l.details['interest_rate'].should.equal(0.05)
def compare_ours_to_history(our_exchange_id, exchange, price_currency, volume_currency='BTC'): hist_trades = get_historical_trades( exchange.lower(), price_currency, volume_currency, ) our_trades = get_our_recorded_trades(our_exchange_id.upper()) start = parse('2015-11-27 0:0:0').datetime.replace(tzinfo=None) end = parse('2015-11-27 11:59:59').datetime.replace(tzinfo=None) print our_exchange_id.upper() hist_in_range = [t for t in hist_trades if t[0] >= start and t[0] <= end] ours_in_range = [t for t in our_trades if t[0] >= start and t[0] <= end] for t in hist_in_range: if t not in ours_in_range: print 'Hist Trade not in ours: %s' % t for t in ours_in_range: if t not in hist_in_range: print'Our trade not in history: %s' % t print'\n\n\n\n\n'
def parse_trades(self, resp_obj): trades = [] for trade in resp_obj: trade = { 'exchange': self.exchange_name, 'price': Money(trade['price'], 'CAD'), 'volume': Money(trade['size'], 'BTC'), 'timestamp': parse(trade['time']).epoch, 'trade_id': trade['trade_id'], } trades.append(trade) return trades
def money_moving(): current_time = Delorean().datetime morning_timeslot = parse('9am -0800').datetime # when we get up in PST afternoon_timeslot = parse( '3:30pm -0500').datetime # 4pm EST is BMO's wire cutoff in_morning_timeslot = (current_time.hour == morning_timeslot.hour and current_time.minute == morning_timeslot.minute) in_afternoon_timeslot = (current_time.hour == afternoon_timeslot.hour and current_time.minute == afternoon_timeslot.minute) if in_morning_timeslot or in_afternoon_timeslot: on_call_dev = get_on_call_dev() channel = '@%s' % on_call_dev if in_morning_timeslot: message = 'Time for early morning money moving!' elif in_afternoon_timeslot: message = 'Time for afternoon money moving! (Wire cutoff is 4pm EST)' slacker = Slacker(channel, 'mover', icon_emoji=':moneybag:') slacker.notify(message)
def get_start_time_and_end_time(self, default_start=None, default_end=None): start = self.get_argument('start', None) end = self.get_argument('end', None) start_time = None end_time = None if start: start_time = parse(start).datetime elif default_start: start_time = default_start else: start_time = Delorean().truncate('day').datetime if end: end_time = parse(end).datetime elif default_end: end_time = default_end else: end_time = Delorean(start_time, 'UTC').next_day(1).datetime return start_time, end_time
def parse_trades(self, resp_obj): raw_trades = resp_obj['recentTrades'] trades = [] for raw_trade in raw_trades: trade = { 'exchange': self.exchange_name, 'price': Money(raw_trade['price'], 'USD'), 'volume': Money(raw_trade['amount'], 'BTC'), 'timestamp': parse(raw_trade['timestamp']).epoch, 'trade_id': raw_trade['matchNumber'], } trades.append(trade) return trades
def get_order_details_resp(self, reqs): try: raw_order, headers = self.resp(reqs['order']) except exceptions.NoEffectOrderCancelledError: # Coinbase returns an API Error with the text "NotFound" in it when # querying orders that were cancelled with no trades, so we return an empty # order here if that is the response. result = { 'time_created': None, 'type': None, 'btc_total': Money('0', self.volume_currency), 'fiat_total': Money('0', self.currency), 'trades': [], } return result # This already has the results (see above) so we don't need to .resp() it our_trades = reqs['trades'] mode = self._order_mode_to_const(raw_order['side']) total_btc = Money(raw_order['filled_size'], 'BTC') time_created = int(parse(raw_order['created_at']).epoch) total_fiat = Money('0', self.currency) for t in our_trades: total_fiat += t['fiat'] result = { 'time_created': time_created, 'type': mode, 'btc_total': total_btc, 'fiat_total': total_fiat, 'trades': our_trades } return result
from gryphon.lib.models.emeraldhavoc.trade import Trade as EHTrade from gryphon.lib.money import Money from gryphon.lib.time_parsing import parse # this is a hack for now because we don't have consistent data # sources for the other exchanges, and I don't want to dirty # gryphon.lib's commit history with something like this. exchanges_with_volume_sources = [ 'cavirtex', 'kraken', 'bitstamp', 'bitfinex', 'vaultofsatoshi', ] EARLIEST_TICKER_ENTRY = parse('2015-11-13 19:30').datetime EARLIEST_TRADE_ENTRY = parse('2015-11-25 23:59').datetime # Functions for getting a single exchange's volume def get_single_exchange_volume_in_period(tc_db, exchange_name, start_time, end_time): if is_past_single_day(start_time, end_time) and start_time > EARLIEST_TICKER_ENTRY: return get_single_exchange_volume_on_day_from_db_tickers( tc_db, exchange_name, start_time, ) elif start_time > EARLIEST_TRADE_ENTRY:
def audit_all_bw_volume(exchange_list): start_date = parse('2015-12-01').datetime end_date = parse('2015-12-15').datetime for exchange in exchange_list: audit_bw_volume(exchange, start_date, end_date)
'KRAKEN', 'COINBASE', 'COINBASE_CAD', 'ITBIT', 'CAVIRTEX', 'QUADRIGA', #'COINSETTER', ] bw_exchanges = [ 'BITSTAMP', 'BITFINEX', 'KRAKEN', ] test_start_date = parse('2015-12-1').datetime test_end_date = parse('2015-12-15').datetime def backfill_trades(exchanges): for e in exchanges: trades = get_trades_to_backfill(e[0], e[1], e[2], e[3]) write_trades_to_db(e[0], trades) def get_trades_to_backfill(our_exchange_id, exchange, price_currency, volume_currency): our_trades = get_our_recorded_trades(our_exchange_id.upper()) oldest_trade_timestamp = our_trades[0][0] historical_trades = get_historical_trades(exchange.lower(), price_currency, volume_currency) trades_to_add = [t for t in historical_trades if t[0] < oldest_trade_timestamp] # sort the trades to add in adding the most recent one first.
from gryphon.lib.gryphonfury import positions from gryphon.lib.logger import get_logger from gryphon.lib.models.exchange import Exchange as ExchangeData from gryphon.lib.models.exchange import Position from gryphon.lib.models.liability import Liability from gryphon.lib.models.order import Order from gryphon.lib.models.trade import Trade from gryphon.lib.models.transaction import Transaction from gryphon.lib.money import Money from gryphon.lib.time_parsing import parse logger = get_logger(__name__) # If your ledger is only accurate after a certain time, put it here, and ledger_balance # requests earlier than this will throw an exception. EARLIEST_CORRECT_LEDGER_DATE = parse('2015-01-01').datetime # This number is used by the overwatch bot to determine if there is a bitcoin amount # missing from our system. # TODO: # - This should be configurable by the user. Since this is only used in overwatch, # maybe it should be part of an overwatch config. # - We should also be able to represent targets in all our supported cryptocurrencies # in this manner. BTC_NET_ASSETS_TARGET = Money('0', 'BTC') NO_BURN_ACCOUNT_WARNING = """\ There is no entry with the name 'BURN' in your exchange account table, so we won't\ be able to show non-trading expenses in the dashboard service. You can learn how to\ set this feature up in the framework documentation.\ """
def _datetime_to_timestamp(self, dt_string): # As of 2015-12-01 13:00 UTC, quadriga timestamps are now in UTC # (with no warning or backwards compatability, of course) return int(parse(dt_string).epoch)