class HWBalance(Base): """A Hot Wallet Balance, for internal use only""" id = sa.Column(sa.Integer, sa.Sequence('hwbalance_id_seq'), primary_key=True) available = sa.Column(LedgerAmount, nullable=False) total = sa.Column(LedgerAmount, nullable=False) currency = sa.Column(sa.String(4), nullable=False) # i.e. BTC, DASH, USDT network = sa.Column(sa.String(64), nullable=False) # i.e. Bitcoin, Dash, Crypto Capital time = sa.Column(sa.DateTime(), default=datetime.datetime.utcnow) def __init__(self, available, total, currency, network): self.available = available self.total = total self.currency = currency self.network = network self.load_commodities() @orm.reconstructor def load_commodities(self): """ Load the commodities for Amounts in this object. """ if isinstance(self.available, Amount): self.available = Amount("{0:.8f} {1}".format(self.available.to_double(), self.currency)) else: self.available = Amount("{0:.8f} {1}".format(self.available, self.currency)) if isinstance(self.total, Amount): self.total = Amount("{0:.8f} {1}".format(self.total.to_double(), self.currency)) else: self.total = Amount("{0:.8f} {1}".format(self.total, self.currency))
def get_balance_summary(session=ses): bals = get_balances(session=session) resp = "\n_______ %s _______\n" % time.asctime(time.gmtime(time.time())) usdtotal = Amount("0 USD") details = {} for amount in bals[0]: comm = str(amount.commodity) if comm == 'USD': inde = Amount("1 USD") details['USD'] = {'index': inde, 'amount': amount} usdtotal = usdtotal + amount else: ticker = get_ticker(market="%s_USD" % comm, red=red) if not ticker: resp += "skipping inactive bal %s\n" % amount continue inde = ticker.calculate_index() details[comm] = {'index': inde, 'amount': Amount("%s USD" % amount.number()) * inde} usdtotal = usdtotal + details[comm]['amount'] resp += "\nTotal Value:\t$%s\n\n" % usdtotal.number() for amount in bals[0]: comm = str(amount.commodity) if comm in details: damount = details[comm]['amount'].to_double() percent = (details[comm]['amount'] / usdtotal * Amount("100 USD")).to_double() resp += "{0:16s}\t==\t${1:8.2f} ({2:3.2f}%)\t@ ${3:8.4f}\n".format(amount, damount, percent, details[comm]['index'].to_double()) return resp
def load_commodities(self): """ Load the commodities for Amounts in this object. """ if isinstance(self.amount, Amount): self.amount = Amount("{0:.8f} {1}".format(self.amount.to_double(), self.currency)) else: self.amount = Amount("{0:.8f} {1}".format(self.amount, self.currency))
def load_commodities(self): """ Load the commodities for Amounts in this object. """ if isinstance(self.available, Amount): self.available = Amount("{0:.8f} {1}".format(self.available.to_double(), self.currency)) else: self.available = Amount("{0:.8f} {1}".format(self.available, self.currency)) if isinstance(self.total, Amount): self.total = Amount("{0:.8f} {1}".format(self.total.to_double(), self.currency)) else: self.total = Amount("{0:.8f} {1}".format(self.total, self.currency))
def attribute_to_partner(partner, partner_string_or_spec, amount): """Return the amount attributable to the given partner.""" try: a = attribute_to_partner_strict(partner, partner_string_or_spec, amount) except ValueError: a = Amount(0) return a
def attribute_to_residual(partner_string, amount, digits_of_precision=2): """Return the residual amount not attributable to any partners.""" spec = parse_partner_string(partner_string) v = round( float(amount) - sum(float(attribute_to_partner(partner, spec, amount)) for partner in spec), digits_of_precision, ) return Amount(str(v)).with_commodity(amount.commodity)
class Credit(Base): """A Credit, which adds tokens to a User's Balance.""" id = sa.Column(sa.Integer, sa.Sequence('credit_id_seq'), primary_key=True) amount = sa.Column(LedgerAmount, nullable=False) address = sa.Column(sa.String(64), nullable=False) # i.e. 1PkzTWAyfR9yoFw2jptKQ3g6E5nKXPsy8r, XhwWxABXPVG5Z3ePyLVA3VixPRkARK6FKy currency = sa.Column(sa.String(4), nullable=False) # i.e. BTC, DASH, USD network = sa.Column(sa.String(64), nullable=False) # i.e. Bitcoin, Dash, Crypto Capital transaction_state = sa.Column(sa.Enum("unconfirmed", "complete", "error", "canceled", name='transaction_state'), nullable=False) reference = sa.Column(sa.String(256), nullable=True) # i.e. invoice#1 ref_id = sa.Column(sa.String(256), nullable=False, unique=True) # i.e. 4cef42f9ff334b9b11bffbd9da21da54176103d92c1c6e4442cbe28ca43540fd:0 time = sa.Column(sa.DateTime(), nullable=False) # foreign key reference to the owner of this user_id = sa.Column( sa.Integer, sa.ForeignKey('user.id'), nullable=False) user = orm.relationship("User", foreign_keys=[user_id]) def __init__(self, amount, address, currency, network, transaction_state, reference, ref_id, user_id, time): self.amount = amount self.address = address self.currency = currency self.network = network self.transaction_state = transaction_state self.reference = reference self.ref_id = ref_id self.user_id = user_id # self.time = pytz.utc.localize(time) self.time = time.replace(tzinfo=None) self.load_commodities() def __repr__(self): return "<Credit(amount=%s, address='%s', currency='%s', network='%s', transaction_state='%s', reference='%s', " \ "ref_id='%s', time=%s)>" % ( self.amount, self.address, self.currency, self.network, self.transaction_state, self.network, self.ref_id, datetime_rfc3339(self.time)) def get_ledger_entry(self): date = self.time.strftime('%Y/%m/%d %H:%M:%S') ledger = "%s %s %s %s\n" % (date, self.reference, 'credit', self.currency) ledger += " Assets:{0}:{1}:credit {2}\n".format(self.network, self.currency, self.amount) ledger += " Equity:Wallet:{0}:debit {1}\n".format(self.currency, -self.amount) ledger += "\n" return ledger @orm.reconstructor def load_commodities(self): """ Load the commodities for Amounts in this object. """ if isinstance(self.amount, Amount): self.amount = Amount("{0:.8f} {1}".format(self.amount.to_double(), self.currency)) else: self.amount = Amount("{0:.8f} {1}".format(self.amount, self.currency))
def get_open_orders(self, market=None): try: rawos = self.bitfinex_request('orders').json() except ValueError as e: self.logger.exception(e) orders = [] for o in rawos: if market is None or o['symbol'] == self.unformat_market(market): side = 'ask' if o['side'] == 'sell' else 'bid' # orders.append(em.LimitOrder(Amount("%s %s" % (o['price'], self.quote_commodity(market))), # Amount("%s %s" % (o['remaining_amount'], self.base_commodity(market))), side, # self.NAME, str(o['id']))) pair = self.format_market(o['symbol']) base = self.base_commodity(pair) amount = Amount("%s %s" % (o['remaining_amount'], base)) exec_amount = Amount("%s %s" % (o['executed_amount'], base)) quote = self.quote_commodity(pair) lo = None try: lo = get_order_by_order_id(str(o['id']), 'bitfinex', session=self.session) except Exception as e: self.logger.exception(e) if lo is None: lo = em.LimitOrder(Amount("%s %s" % (o['price'], quote)), amount, pair, side, self.NAME, str(o['id']), exec_amount=exec_amount, state='open') self.session.add(lo) else: lo.state = 'open' orders.append(lo) try: self.session.commit() except Exception as e: self.logger.exception(e) self.session.rollback() self.session.flush() return orders
class Balance(Base): """A user's balance in a single currency. Only the latest record is valid.""" id = sa.Column(sa.Integer, sa.Sequence('balance_id_seq'), primary_key=True) total = sa.Column(LedgerAmount, nullable=False) available = sa.Column(LedgerAmount, nullable=False) currency = sa.Column(sa.String(4), nullable=False) # i.e. BTC, DASH, USD time = sa.Column(sa.DateTime(), default=datetime.datetime.utcnow) reference = sa.Column(sa.String(256), nullable=True) # foreign key reference to the owner of this user_id = sa.Column( sa.Integer, sa.ForeignKey('user.id'), nullable=False) user = orm.relationship("User", foreign_keys=[user_id]) def __init__(self, total, available, currency, reference, user_id, time=None): self.total = total self.available = available self.currency = currency self.reference = reference self.user_id = user_id self.time = time if time is not None else datetime.datetime.utcnow() self.load_commodities() def __repr__(self): return "<Balance(total=%s, available=%s, currency='%s', reference='%s', user_id=%s, time=%s)>" % ( self.total, self.available, self.currency, self.reference, self.user_id, datetime_rfc3339(self.time)) @orm.reconstructor def load_commodities(self): """ Load the commodities for Amounts in this object. """ if isinstance(self.available, Amount): self.available = Amount("{0:.8f} {1}".format(self.available.to_double(), self.currency)) else: self.available = Amount("{0:.8f} {1}".format(self.available, self.currency)) if isinstance(self.total, Amount): self.total = Amount("{0:.8f} {1}".format(self.total.to_double(), self.currency)) else: self.total = Amount("{0:.8f} {1}".format(self.total, self.currency))
def sync_balances(self): try: data = self.bitfinex_request('balances').json() except ValueError as e: self.logger.exception( '%s %s while sending to bitfinex get_balance' % (type(e), str(e))) if 'message' in data: self.logger.exception('%s while sending to bitfinex get_balance' % data['message']) self.logger.debug('balances data %s' % data) self.logger.debug('self.active_currencies %s' % self.active_currencies) total = Balance() available = Balance() for bal in data: comm = self.format_commodity(bal['currency']) total = total + Amount("%s %s" % (bal['amount'], comm)) available = available + Amount("%s %s" % (bal['available'], comm)) self.logger.debug("total balance: %s" % total) self.logger.debug("available balance: %s" % available) bals = {} for amount in total: comm = str(amount.commodity) bals[comm] = self.session.query(wm.Balance).filter(wm.Balance.user_id == self.manager_user.id) \ .filter(wm.Balance.currency == comm).one_or_none() if not bals[comm]: bals[comm] = wm.Balance( amount, available.commodity_amount(amount.commodity), comm, "", self.manager_user.id) self.session.add(bals[comm]) else: bals[comm].load_commodities() bals[comm].total = amount bals[comm].available = available.commodity_amount( amount.commodity) try: self.session.commit() except Exception as e: self.logger.exception(e) self.session.rollback() self.session.flush()
def attribute_to_partner_strict(partner, partner_string_or_spec, amount): """Return the amount attributable to the given partner.""" spec = ( partner_string_or_spec if isinstance(partner_string_or_spec, dict) else parse_partner_string(partner_string_or_spec) ) if partner not in spec: raise ValueError("Partner not found in partner string: %s" % partner) v100 = spec[partner] * float(amount.abs()) f_floor = round if isclose(v100, round(v100)) else floor v = amount.sign() * 0.01 * f_floor(v100) return Amount(str(v)).with_commodity(amount.commodity)
def sync_credits(self, rescan=False): for cur in self.active_currencies.union(set(["DRK"])): allknown = False end = time.time() while not allknown: history = self.get_dw_history(cur, end=end) if len(history) == 0: break allknown = True for row in history: if row['status'] != 'COMPLETED': continue rtype = row['type'].lower() found = 0 if rtype == "withdrawal": found = self.session.query(wm.Debit) \ .filter(wm.Debit.ref_id == 'bitfinex|%s' % row['id']) \ .count() elif rtype == "deposit": found = self.session.query(wm.Credit) \ .filter(wm.Credit.ref_id == 'bitfinex|%s' % row['id']) \ .count() if found != 0: print "; %s already known" % row['id'] continue allknown = False if float(row['timestamp']) < end: end = float(row['timestamp']) dtime = datetime.datetime.fromtimestamp( float(row['timestamp'])) asset = self.format_commodity(row['currency']) amount = Amount("%s %s" % (row['amount'], asset)) if row['status'] == 'COMPLETED': status = 'complete' elif row['status'] == 'CANCELED': status = 'canceled' else: status = 'unconfirmed' if rtype == "withdrawal": self.session.add( wm.Debit(amount, 0, row['address'], asset, "bitfinex", status, "bitfinex", "bitfinex|%s" % row['id'], self.manager_user.id, dtime)) elif rtype == "deposit": self.session.add( wm.Credit(amount, row['address'], asset, "bitfinex", status, "bitfinex", "bitfinex|%s" % row['id'], self.manager_user.id, dtime)) self.session.commit()
def fib_fan(side, amount, ticker, session): def calc_price(sid, index, offset): if sid == 'ask': return index * (Amount("1 %s" % index.commodity) + Amount("%s %s" % (offset, index.commodity)) / 100) else: return index * (Amount("1 %s" % index.commodity) - Amount("%s %s" % (offset, index.commodity)) / 100) usdamount = get_usd_value(amount) if usdamount <= MINORDER: print "ignoring dusty order %s worth %s" % (usdamount, usdamount) return fibseq = [1, 2, 3, 5, 8, 13] index = ticker.calculate_index() base = ticker.market.split("_")[0] if usdamount / Amount("{0} USD".format(len(fibseq))) <= MINORDER: price = calc_price(side, index, fibseq[int(round(len(fibseq) / 2))]) if side == 'bid': amount = Amount("{0:.8f} {1}".format(amount.to_double(), base)) / \ Amount("{0:.8f} {1}".format(index.to_double(), base)) # usdval = get_usd_value(amount) # print "{0} {1} @ {2:0.6f} {3} worth ${4:0.2f})".format(side, amount, price.to_double(), ticker.market, # usdval.to_double()) create_order(ticker.exchange, price=price, amount=amount, market=ticker.market, side=side, session=session) else: if side == 'bid': amount = Amount("{0:.8f} {1}".format((amount / len(fibseq)).to_double(), base)) / \ Amount("{0:.8f} {1}".format(index.to_double(), base)) else: amount /= len(fibseq) for fib in fibseq: price = calc_price(side, index, fib) create_order(ticker.exchange, price=price, amount=amount, market=ticker.market, side=side, session=session) # usdval = get_usd_value(amount) * price / index # print "{0} {1} @ {2:0.6f} {3} worth ${4:0.2f})".format(side, amount, price.to_double(), # ticker.market, usdval.to_double()) sync_balances(ticker.exchange)
def test_cancel_order_order_id_no_prefix(self): bitfinex.sync_orders() order = create_order('bitfinex', 100, 0.1, 'BTC_USD', 'bid', session=bitfinex.session, expire=time.time() + 60) assert isinstance(order.id, int) assert isinstance(order.price, Amount) assert order.price == Amount("100 USD") assert order.state == 'pending' oorder = get_orders(oid=order.id, session=bitfinex.session) countdown = 1000 while oorder[0].state == 'pending' and countdown > 0: countdown -= 1 oorder = get_orders(oid=order.id, session=bitfinex.session) if oorder[0].state == 'pending': time.sleep(0.01) bitfinex.session.close() assert len(oorder) == 1 assert oorder[0].state == 'open' print oorder[0].order_id.split("|")[1] cancel_orders('bitfinex', order_id=oorder[0].order_id.split("|")[1]) #corder = get_orders(oid=order.id, session=bitfinex.session) corder = get_order_by_order_id(oorder[0].order_id, 'bitfinex', bitfinex.session) countdown = 1000 while (corder is None or corder.state != 'closed') and countdown > 0: countdown -= 1 #corder = get_orders(oid=order.id, session=bitfinex.session) corder = get_order_by_order_id(oorder[0].order_id, 'bitfinex', bitfinex.session) if (corder is None or corder.state != 'closed'): time.sleep(0.01) bitfinex.session.close() assert corder.state == 'closed'
def test_order_lifecycle(self): order = create_order('bitfinex', 100, 0.01, 'BTC_USD', 'bid', session=bitfinex.session, expire=time.time() + 60) assert isinstance(order.id, int) assert isinstance(order.price, Amount) assert order.price == Amount("100 USD") assert order.state == 'pending' oorder = get_orders(oid=order.id, session=bitfinex.session) countdown = 1000 while oorder[0].state == 'pending' and countdown > 0: countdown -= 1 oorder = get_orders(oid=order.id, session=bitfinex.session) if oorder[0].state == 'pending': time.sleep(0.01) bitfinex.session.close() assert len(oorder) == 1 assert oorder[0].state == 'open' bitfinex.session.close() cancel_orders('bitfinex', oid=order.id) countdown = 1000 corder = get_orders('bitfinex', order_id=oorder[0].order_id, session=bitfinex.session) while corder[0].state != 'closed' and countdown > 0: countdown -= 1 corder = get_orders('bitfinex', order_id=oorder[0].order_id, session=bitfinex.session) if corder[0].state != 'closed': time.sleep(0.01) bitfinex.session.close() assert len(corder) == 1 assert corder[0].state == 'closed'
def test_money_cycle(): fee = Amount("%s BTC" % CFG.get('internal', 'FEE')) amount = Amount("%s BTC" % 0.01) # Receive Internal to user addy = client.get_model('Address')(currency='BTC', network='Internal') address = client.address.createAddress(address=addy).result() internal_credit(address.address, amount + fee, session=ses) for i in range(0, 60): c = ses.query(wm.Credit).filter(wm.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.address == address.address assert c.amount == amount + fee assert c.currency == 'BTC' assert c.network == 'Internal' assert len(c.ref_id) > 0 # ses.close() bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total > Amount("0 BTC") assert bal.available == Amount("0 BTC") bal.available = bal.available + c.amount ses.add(bal) try: ses.commit() except Exception as e: ses.rollback() print "skipping test" return ses.close() # send Internal internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client.debit.sendMoney(debit={'amount': 0.01, 'fee': fee.to_double(), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': ''}).result() assert debit.transaction_state == 'complete' assert debit.amount == 0.01 assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(wm.Credit).filter(wm.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.transaction_state == 'complete' assert c.amount == Amount("0.01 BTC") assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0 BTC") assert bal.available == Amount("0 BTC") bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user2.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0.01 BTC") assert bal.available == Amount("0.01 BTC") ses.close() # send BTC internally to user 2 addy = client2.get_model('Address')(currency='BTC', network='Internal') address = client2.address.createAddress(address=addy).result() debit = client2.debit.sendMoney(debit={'amount': 0.0099, 'fee': CFG.get('internal', 'FEE'), 'address': address.address, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal internal', 'ref_id': ''}).result() assert debit.transaction_state == 'complete' assert debit.amount == 0.0099 assert debit.reference == 'test send money internal internal' assert debit.network == 'internal' for i in range(0, 60): c = ses.query(wm.Credit).filter(wm.Credit.address == address.address).first() if c is not None: break else: time.sleep(1) assert c is not None assert c.transaction_state == 'complete' assert c.amount == Amount("0.0099 BTC") assert c.reference == 'test send money internal internal' assert c.network == 'internal' assert int(debit.ref_id) == c.id assert int(c.ref_id) == debit.id bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0 BTC") assert bal.available == Amount("0 BTC") bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user2.id).filter(wm.Balance.currency == 'BTC').first() assert bal.total == Amount("0.0099 BTC") assert bal.available == Amount("0.0099 BTC") ses.close() # Send Internal from user2 addy = internal_address() debit = client2.debit.sendMoney(debit={'amount': 0.0098, 'fee': CFG.get('internal', 'FEE'), 'address': addy, 'currency': 'BTC', 'network': 'Internal', 'state': 'unconfirmed', 'reference': 'test send money internal', 'ref_id': ''}).result() time.sleep(0.1) for i in range(0, 60): d = ses.query(wm.Debit).filter(wm.Debit.address == addy).first() if d is not None: break else: time.sleep(1) assert d is not None assert d.address == addy assert d.amount == Amount("0.0098 BTC") bal = ses.query(wm.Balance).filter(wm.Balance.user_id == user2.id).filter(wm.Balance.currency == 'BTC') assert bal.first().total == Amount("0 BTC") assert bal.first().available == Amount("0 BTC")
if category.startswith("From '"): continue if not expenseacct in monefy_account_shorthands: print("%s not in %s" % (expenseacct, monefy_account_shorthands)) assert (False) targetacct = monefy_account_shorthands[expenseacct] description, *commentlist = csvdescription.split(";") description = description.strip() comment = " ".join(commentlist).strip() if comment == "": comment = None new_transaction = Transaction(description, date).addPosting( Posting(targetacct, Amount(amountCmp, currency))) if not comment is None and len(comment) > 0: new_transaction tm = transfer_regex_.search(category) if not tm is None: sourceacct = tm.group(1) if not sourceacct in monefy_account_shorthands: print("unknown sourceacct:", sourceacct, file=sys.stderr) assert (False) sourceacct = monefy_account_shorthands[sourceacct] if description == "Assert": new_transaction = Transaction(description, date).addPosting( Posting(sourceacct, Amount(0, currency)).addPostPostingAssertAmount( Amount(-1 * amountCmp, currency))).addTag("monefy")
file=sys.stderr) assert (False) ## if no match found, tag as default transaction if not isinstance(new_transaction, Transaction): if amountCmp > 0.0: new_transaction = unknown_revenue_default_transaction_.copy() elif amountCmp < 0.0: new_transaction = unknown_expense_default_transaction_.copy() else: new_transaction = unknown_equity_default_transaction_.copy() ## Convert FutureAmoutFractions to real Amounts for p in new_transaction.postings: if isinstance(p.amount, FutureAmountFraction): p.amount.convertToAmount(Amount(amountCmp, currency)) if date2 != date: p.setDate(date2) new_transaction.prependPosting( Posting(elba_primary_account_, Amount(amountCmp, currency))).addComment(description).setDate(date) transactions.append(new_transaction) not_balanced = [t for t in transactions if not t.isBalanced()] if len(not_balanced) > 0: print("ERROR: the following transactions are not balanced !!\n", file=sys.stderr) print("\n\n".join(map(str, (sortTransactionsByDate(not_balanced)))), file=sys.stderr)