Skip to content

Commit

Permalink
Merge branch 'development' into refactor/add_bvnd
Browse files Browse the repository at this point in the history
  • Loading branch information
PtrckM authored Mar 27, 2021
2 parents 4ca95ab + f4167b1 commit 87676a4
Show file tree
Hide file tree
Showing 47 changed files with 3,434 additions and 92 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ We created hummingbot to promote **decentralized market-making**: enabling membe
| <img src="assets/cryptocom_logo.png" alt="Crypto.com" width="90" /> | crypto_com | [Crypto.com](https://crypto.com/exchange) | 2 | [API](https://exchange-docs.crypto.com/#introduction) |![YELLOW](https://via.placeholder.com/15/ffff00/?text=+) |
| <img src="assets/dydx_logo.png" alt="DyDx" width="90" /> | dydx | [dy/dx](https://dydx.exchange/) | 1 | [API](https://docs.dydx.exchange/) |![GREEN](https://via.placeholder.com/15/008000/?text=+) |
| <img src="assets/eterbase_logo.png" alt="Eterbase" width="90" /> | eterbase | [Eterbase](https://www.eterbase.com/) | * | [API](https://developers.eterbase.exchange/?version=latest) |![RED](https://via.placeholder.com/15/f03c15/?text=+) |
| <img src="assets/hitbtc_logo.png" alt="HitBTC" width="90" /> | hitbtc | [HitBTC](https://hitbtc.com/) | 2 | [API](https://api.hitbtc.com/) | ![YELLOW](https://via.placeholder.com/15/ffff00/?text=+) |
|<img src="assets/huobi_logo.png" alt="Huobi Global" width="90" />| huobi | [Huobi Global](https://www.hbg.com) | 1 | [API](https://huobiapi.github.io/docs/spot/v1/en/) |![GREEN](https://via.placeholder.com/15/008000/?text=+) |
| <img src="assets/kucoin_logo.png" alt="KuCoin" width="90" /> | kucoin | [KuCoin](https://www.kucoin.com/) | 1 | [API](https://docs.kucoin.com/#general) |![GREEN](https://via.placeholder.com/15/008000/?text=+) |
| <img src="assets/kraken_logo.png" alt="Kraken" width="90" /> | kraken | [Kraken](https://www.kraken.com/) | 1 | [API](https://www.kraken.com/features/api) |![GREEN](https://via.placeholder.com/15/008000/?text=+) |
Expand Down
Binary file added assets/hitbtc_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
crypto_com_api_key = os.getenv("CRYPTO_COM_API_KEY")
crypto_com_secret_key = os.getenv("CRYPTO_COM_SECRET_KEY")
# HitBTC Tests
hitbtc_api_key = os.getenv("HITBTC_API_KEY")
hitbtc_secret_key = os.getenv("HITBTC_SECRET_KEY")
# Wallet Tests
test_erc20_token_address = os.getenv("TEST_ERC20_TOKEN_ADDRESS")
web3_test_private_key_a = os.getenv("TEST_WALLET_PRIVATE_KEY_A")
Expand Down
4 changes: 3 additions & 1 deletion hummingbot/client/command/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .open_orders_command import OpenOrdersCommand
from .trades_command import TradesCommand
from .pnl_command import PnlCommand
from .rate_command import RateCommand


__all__ = [
Expand All @@ -42,5 +43,6 @@
GenerateCertsCommand,
OpenOrdersCommand,
TradesCommand,
PnlCommand
PnlCommand,
RateCommand,
]
49 changes: 18 additions & 31 deletions hummingbot/client/command/balance_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
)
from hummingbot.client.config.config_validators import validate_decimal, validate_exchange
from hummingbot.market.celo.celo_cli import CeloCLI
from hummingbot.core.utils.market_price import usd_value
from hummingbot.client.performance import smart_round
from hummingbot.core.rate_oracle.rate_oracle import RateOracle
import pandas as pd
from decimal import Decimal
from typing import TYPE_CHECKING, Dict, Optional, List
Expand Down Expand Up @@ -76,6 +77,7 @@ def balance(self,
save_to_yml(file_path, config_map)

async def show_balances(self):
total_col_name = f'Total ({RateOracle.global_token_symbol})'
self._notify("Updating balances, please wait...")
all_ex_bals = await UserBalances.instance().all_balances_all_exchanges()
all_ex_avai_bals = UserBalances.instance().all_avai_balances_all_exchanges()
Expand All @@ -88,18 +90,17 @@ async def show_balances(self):

for exchange, bals in all_ex_bals.items():
self._notify(f"\n{exchange}:")
# df = await self.exchange_balances_df(bals, all_ex_limits.get(exchange, {}))
df, allocated_total = await self.exchange_balances_usd_df(bals, all_ex_avai_bals.get(exchange, {}))
df, allocated_total = await self.exchange_balances_extra_df(bals, all_ex_avai_bals.get(exchange, {}))
if df.empty:
self._notify("You have no balance on this exchange.")
else:
lines = [" " + line for line in df.to_string(index=False).split("\n")]
self._notify("\n".join(lines))
self._notify(f"\n Total: $ {df['Total ($)'].sum():.0f} "
f"Allocated: {allocated_total / df['Total ($)'].sum():.2%}")
exchanges_total += df['Total ($)'].sum()
self._notify(f"\n Total: {RateOracle.global_token_symbol} {smart_round(df[total_col_name].sum())} "
f"Allocated: {allocated_total / df[total_col_name].sum():.2%}")
exchanges_total += df[total_col_name].sum()

self._notify(f"\n\nExchanges Total: $ {exchanges_total:.0f} ")
self._notify(f"\n\nExchanges Total: {RateOracle.global_token_symbol} {exchanges_total:.0f} ")

celo_address = global_config_map["celo_address"].value
if celo_address is not None:
Expand All @@ -126,41 +127,27 @@ async def show_balances(self):
self._notify("\nxdai:")
self._notify("\n".join(lines))

async def exchange_balances_df(self, # type: HummingbotApplication
exchange_balances: Dict[str, Decimal],
exchange_limits: Dict[str, str]):
rows = []
for token, bal in exchange_balances.items():
limit = Decimal(exchange_limits.get(token.upper(), 0)) if exchange_limits is not None else Decimal(0)
if bal == Decimal(0) and limit == Decimal(0):
continue
token = token.upper()
rows.append({"Asset": token.upper(),
"Amount": round(bal, 4),
"Limit": round(limit, 4) if limit > Decimal(0) else "-"})
df = pd.DataFrame(data=rows, columns=["Asset", "Amount", "Limit"])
df.sort_values(by=["Asset"], inplace=True)
return df

async def exchange_balances_usd_df(self, # type: HummingbotApplication
ex_balances: Dict[str, Decimal],
ex_avai_balances: Dict[str, Decimal]):
async def exchange_balances_extra_df(self, # type: HummingbotApplication
ex_balances: Dict[str, Decimal],
ex_avai_balances: Dict[str, Decimal]):
total_col_name = f"Total ({RateOracle.global_token_symbol})"
allocated_total = Decimal("0")
rows = []
for token, bal in ex_balances.items():
if bal == Decimal(0):
continue
avai = Decimal(ex_avai_balances.get(token.upper(), 0)) if ex_avai_balances is not None else Decimal(0)
allocated = f"{(bal - avai) / bal:.0%}"
usd = await usd_value(token, bal)
usd = 0 if usd is None else usd
allocated_total += await usd_value(token, (bal - avai))
rate = await RateOracle.global_rate(token)
rate = Decimal("0") if rate is None else rate
global_value = rate * bal
allocated_total += rate * (bal - avai)
rows.append({"Asset": token.upper(),
"Total": round(bal, 4),
"Total ($)": round(usd),
total_col_name: smart_round(global_value),
"Allocated": allocated,
})
df = pd.DataFrame(data=rows, columns=["Asset", "Total", "Total ($)", "Allocated"])
df = pd.DataFrame(data=rows, columns=["Asset", "Total", total_col_name, "Allocated"])
df.sort_values(by=["Asset"], inplace=True)
return df, allocated_total

Expand Down
5 changes: 4 additions & 1 deletion hummingbot/client/command/config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@
"gateway_cert_passphrase",
"gateway_api_host",
"gateway_api_port",
"balancer_max_swaps"]
"balancer_max_swaps",
"rate_oracle_source",
"global_token",
"global_token_symbol"]


class ConfigCommand:
Expand Down
16 changes: 9 additions & 7 deletions hummingbot/client/command/open_orders_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from datetime import timezone
from hummingbot.core.utils.async_utils import safe_ensure_future
from hummingbot.core.data_type.common import OpenOrder
from hummingbot.core.utils.market_price import usd_value, get_binance_mid_price
from hummingbot.core.utils.market_price import get_binance_mid_price
from hummingbot.core.rate_oracle.rate_oracle import RateOracle

s_float_0 = float(0)
s_decimal_0 = Decimal("0")
Expand All @@ -30,6 +31,7 @@ async def open_orders_report(self, # type: HummingbotApplication
full_report: bool):
exchange = "binance"
connector = await self.get_binance_connector()
g_sym = RateOracle.global_token_symbol
if connector is None:
self._notify("This command supports only binance (for now), please first connect to binance.")
return
Expand All @@ -39,31 +41,31 @@ async def open_orders_report(self, # type: HummingbotApplication
return
orders = sorted(orders, key=lambda x: (x.trading_pair, x.is_buy))
data = []
columns = ["Market", " Side", " Spread", " Size ($)", " Age"]
columns = ["Market", " Side", " Spread", f" Size ({g_sym})", " Age"]
if full_report:
columns.extend([" Allocation", " Per Total"])
cur_balances = await self.get_current_balances(exchange)
total_value = 0
for o in orders:
total_value += await usd_value(o.trading_pair.split("-")[0], o.amount)
total_value += await RateOracle.global_value(o.trading_pair.split("-")[0], o.amount)
for order in orders:
base, quote = order.trading_pair.split("-")
side = "buy" if order.is_buy else "sell"
mid_price = await get_binance_mid_price(order.trading_pair)
spread = abs(order.price - mid_price) / mid_price
size_usd = await usd_value(order.trading_pair.split("-")[0], order.amount)
size_global = await RateOracle.global_value(order.trading_pair.split("-")[0], order.amount)
age = pd.Timestamp((datetime.utcnow().replace(tzinfo=timezone.utc).timestamp() * 1e3 - order.time) / 1e3,
unit='s').strftime('%H:%M:%S')
data_row = [order.trading_pair, side, f"{spread:.2%}", round(size_usd), age]
data_row = [order.trading_pair, side, f"{spread:.2%}", round(size_global), age]
if full_report:
token = quote if order.is_buy else base
token_value = order.amount * order.price if order.is_buy else order.amount
per_bal = token_value / cur_balances[token]
token_txt = f"({token})"
data_row.extend([f"{per_bal:.0%} {token_txt:>6}", f"{size_usd / total_value:.0%}"])
data_row.extend([f"{per_bal:.0%} {token_txt:>6}", f"{size_global / total_value:.0%}"])
data.append(data_row)
lines = []
orders_df: pd.DataFrame = pd.DataFrame(data=data, columns=columns)
lines.extend([" " + line for line in orders_df.to_string(index=False).split("\n")])
self._notify("\n" + "\n".join(lines))
self._notify(f"\n Total: $ {total_value:.0f}")
self._notify(f"\n Total: {g_sym} {total_value:.0f}")
14 changes: 8 additions & 6 deletions hummingbot/client/command/pnl_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
from hummingbot.core.utils.async_utils import safe_ensure_future
from hummingbot.client.config.security import Security
from hummingbot.user.user_balances import UserBalances
from hummingbot.core.utils.market_price import usd_value
from hummingbot.core.data_type.trade import Trade
from hummingbot.core.data_type.common import OpenOrder
from hummingbot.client.performance import calculate_performance_metrics
from hummingbot.client.command.history_command import get_timestamp
from hummingbot.client.config.global_config_map import global_config_map
from hummingbot.core.rate_oracle.rate_oracle import RateOracle

s_float_0 = float(0)
s_decimal_0 = Decimal("0")
Expand Down Expand Up @@ -73,16 +73,17 @@ async def pnl_report(self, # type: HummingbotApplication
markets = set(global_config_map["binance_markets"].value.split(","))
markets = sorted(markets)
data = []
columns = ["Market", " Traded ($)", " Fee ($)", " PnL ($)", " Return %"]
g_sym = RateOracle.global_token_symbol
columns = ["Market", f" Traded ({g_sym})", f" Fee ({g_sym})", f" PnL ({g_sym})", " Return %"]
for market in markets:
base, quote = market.split("-")
trades: List[Trade] = await connector.get_my_trades(market, days)
if not trades:
continue
perf = await calculate_performance_metrics(exchange, market, trades, cur_balances)
volume = await usd_value(quote, abs(perf.b_vol_quote) + abs(perf.s_vol_quote))
fee = await usd_value(quote, perf.fee_in_quote)
pnl = await usd_value(quote, perf.total_pnl)
volume = await RateOracle.global_value(quote, abs(perf.b_vol_quote) + abs(perf.s_vol_quote))
fee = await RateOracle.global_value(quote, perf.fee_in_quote)
pnl = await RateOracle.global_value(quote, perf.total_pnl)
data.append([market, round(volume, 2), round(fee, 2), round(pnl, 2), f"{perf.return_pct:.2%}"])
if not data:
self._notify(f"No trades during the last {days} day(s).")
Expand All @@ -91,4 +92,5 @@ async def pnl_report(self, # type: HummingbotApplication
df: pd.DataFrame = pd.DataFrame(data=data, columns=columns)
lines.extend([" " + line for line in df.to_string(index=False).split("\n")])
self._notify("\n" + "\n".join(lines))
self._notify(f"\n Total PnL: $ {df[' PnL ($)'].sum():.2f} Total fee: $ {df[' Fee ($)'].sum():.2f}")
self._notify(f"\n Total PnL: {g_sym} {df[f' PnL ({g_sym})'].sum():.2f} "
f"Total fee: {g_sym} {df[f' Fee ({g_sym})'].sum():.2f}")
50 changes: 50 additions & 0 deletions hummingbot/client/command/rate_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from decimal import Decimal
import threading
from typing import (
TYPE_CHECKING,
)
from hummingbot.core.utils.async_utils import safe_ensure_future
from hummingbot.core.rate_oracle.rate_oracle import RateOracle

s_float_0 = float(0)
s_decimal_0 = Decimal("0")

if TYPE_CHECKING:
from hummingbot.client.hummingbot_application import HummingbotApplication


class RateCommand:
def rate(self, # type: HummingbotApplication
pair: str,
token: str
):
if threading.current_thread() != threading.main_thread():
self.ev_loop.call_soon_threadsafe(self.trades)
return
if pair:
safe_ensure_future(self.show_rate(pair))
elif token:
safe_ensure_future(self.show_token_value(token))

async def show_rate(self, # type: HummingbotApplication
pair: str,
):
pair = pair.upper()
self._notify(f"Source: {RateOracle.source.name}")
rate = await RateOracle.rate_async(pair)
if rate is None:
self._notify("Rate is not available.")
return
base, quote = pair.split("-")
self._notify(f"1 {base} = {rate} {quote}")

async def show_token_value(self, # type: HummingbotApplication
token: str
):
token = token.upper()
self._notify(f"Source: {RateOracle.source.name}")
rate = await RateOracle.global_rate(token)
if rate is None:
self._notify("Rate is not available.")
return
self._notify(f"1 {token} = {RateOracle.global_token_symbol} {rate} {RateOracle.global_token}")
13 changes: 8 additions & 5 deletions hummingbot/client/command/trades_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
)
from datetime import datetime
from hummingbot.core.utils.async_utils import safe_ensure_future
from hummingbot.core.utils.market_price import usd_value
from hummingbot.core.data_type.trade import Trade, TradeType
from hummingbot.core.data_type.common import OpenOrder
from hummingbot.client.performance import smart_round
from hummingbot.client.command.history_command import get_timestamp
from hummingbot.client.config.global_config_map import global_config_map
from hummingbot.core.rate_oracle.rate_oracle import RateOracle

s_float_0 = float(0)
s_decimal_0 = Decimal("0")
Expand Down Expand Up @@ -58,31 +58,34 @@ async def market_trades_report(self, # type: HummingbotApplication
days: float,
market: str):
trades: List[Trade] = await connector.get_my_trades(market, days)
g_sym = RateOracle.global_token_symbol
if not trades:
self._notify(f"There is no trade on {market}.")
return
data = []
columns = ["Time", " Side", " Price", "Amount", " Amount ($)"]
amount_g_col_name = f" Amount ({g_sym})"
columns = ["Time", " Side", " Price", "Amount", amount_g_col_name]
trades = sorted(trades, key=lambda x: (x.trading_pair, x.timestamp))
fees = {} # a dict of token and total fee amount
fee_usd = 0

for trade in trades:
time = f"{datetime.fromtimestamp(trade.timestamp / 1e3).strftime('%Y-%m-%d %H:%M:%S')} "
side = "buy" if trade.side is TradeType.BUY else "sell"
usd = await usd_value(trade.trading_pair.split("-")[0], trade.amount)
usd = await RateOracle.global_value(trade.trading_pair.split("-")[0], trade.amount)
data.append([time, side, smart_round(trade.price), smart_round(trade.amount), round(usd)])
for fee in trade.trade_fee.flat_fees:
if fee[0] not in fees:
fees[fee[0]] = fee[1]
else:
fees[fee[0]] += fee[1]
fee_usd += await usd_value(fee[0], fee[1])
fee_usd += await RateOracle.global_value(fee[0], fee[1])

lines = []
df: pd.DataFrame = pd.DataFrame(data=data, columns=columns)
lines.extend([f" {market.upper()}"])
lines.extend([" " + line for line in df.to_string(index=False).split("\n")])
self._notify("\n" + "\n".join(lines))
fee_text = ",".join(k + ": " + f"{v:.4f}" for k, v in fees.items())
self._notify(f"\n Total traded: $ {df[' Amount ($)'].sum():.0f} Fees: {fee_text} ($ {fee_usd:.2f})")
self._notify(f"\n Total traded: {g_sym} {df[amount_g_col_name].sum():.0f} "
f"Fees: {fee_text} ({g_sym} {fee_usd:.2f})")
2 changes: 1 addition & 1 deletion hummingbot/client/config/config_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def get_eth_wallet_private_key() -> Optional[str]:
return account.privateKey.hex()


@lru_cache
@lru_cache(None)
def get_erc20_token_addresses() -> Dict[str, List]:
token_list_url = global_config_map.get("ethereum_token_list_url").value
address_file_path = TOKEN_ADDRESSES_FILE_PATH
Expand Down
Loading

0 comments on commit 87676a4

Please sign in to comment.