Skip to content

Commit

Permalink
Merge branch 'development' into feat/avellaneda_stoikov
Browse files Browse the repository at this point in the history
  • Loading branch information
CrimsonJacket authored Apr 2, 2021
2 parents 87eabd5 + 81e2770 commit 84f175d
Show file tree
Hide file tree
Showing 45 changed files with 544 additions and 194 deletions.
2 changes: 2 additions & 0 deletions hummingbot/client/command/create_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,11 @@ async def prompt_a_config(self, # type: HummingbotApplication

if self.app.to_stop_config:
return
config.value = parse_cvar_value(config, input_value)
err_msg = await config.validate(input_value)
if err_msg is not None:
self._notify(err_msg)
config.value = None
await self.prompt_a_config(config)
else:
config.value = parse_cvar_value(config, input_value)
Expand Down
16 changes: 12 additions & 4 deletions hummingbot/client/command/rate_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
)
from hummingbot.core.utils.async_utils import safe_ensure_future
from hummingbot.core.rate_oracle.rate_oracle import RateOracle
from hummingbot.client.errors import OracleRateUnavailable

s_float_0 = float(0)
s_decimal_0 = Decimal("0")
Expand All @@ -29,14 +30,21 @@ def rate(self, # type: HummingbotApplication
async def show_rate(self, # type: HummingbotApplication
pair: str,
):
try:
msg = await RateCommand.oracle_rate_msg(pair)
except OracleRateUnavailable:
msg = "Rate is not available."
self._notify(msg)

@staticmethod
async def oracle_rate_msg(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
raise OracleRateUnavailable
base, quote = pair.split("-")
self._notify(f"1 {base} = {rate} {quote}")
return f"Source: {RateOracle.source.name}\n1 {base} = {rate} {quote}"

async def show_token_value(self, # type: HummingbotApplication
token: str
Expand Down
52 changes: 42 additions & 10 deletions hummingbot/client/command/start_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@
from hummingbot.client.config.config_helpers import (
get_strategy_starter_file,
)
from hummingbot.client.settings import (
STRATEGIES,
SCRIPTS_PATH,
required_exchanges,
)
import hummingbot.client.settings as settings
from hummingbot.core.utils.async_utils import safe_ensure_future
from hummingbot.core.utils.kill_switch import KillSwitch
from typing import TYPE_CHECKING
from hummingbot.client.config.global_config_map import global_config_map
from hummingbot.script.script_iterator import ScriptIterator
from hummingbot.connector.connector_status import get_connector_status, warning_messages
from hummingbot.client.config.config_var import ConfigVar
from hummingbot.client.command.rate_command import RateCommand
from hummingbot.client.config.config_validators import validate_bool
from hummingbot.client.errors import OracleRateUnavailable
from hummingbot.core.rate_oracle.rate_oracle import RateOracle
if TYPE_CHECKING:
from hummingbot.client.hummingbot_application import HummingbotApplication

Expand Down Expand Up @@ -57,15 +58,19 @@ def start(self, # type: HummingbotApplication
async def start_check(self, # type: HummingbotApplication
log_level: Optional[str] = None,
restore: Optional[bool] = False):

if self.strategy_task is not None and not self.strategy_task.done():
self._notify('The bot is already running - please run "stop" first')
return

if settings.required_rate_oracle:
if not (await self.confirm_oracle_conversion_rate()):
self._notify("The strategy failed to start.")
return
else:
RateOracle.get_instance().start()
is_valid = await self.status_check_all(notify_success=False)
if not is_valid:
return

if self._last_started_strategy_file != self.strategy_file_name:
init_logging("hummingbot_logs.yml",
override_log_level=log_level.upper() if log_level else None,
Expand All @@ -83,7 +88,7 @@ async def start_check(self, # type: HummingbotApplication
if global_config_map.get("paper_trade_enabled").value:
self._notify("\nPaper Trading ON: All orders are simulated, and no real orders are placed.")

for exchange in required_exchanges:
for exchange in settings.required_exchanges:
connector = str(exchange)
status = get_connector_status(connector)

Expand All @@ -104,7 +109,7 @@ async def start_market_making(self, # type: HummingbotApplication
strategy_name: str,
restore: Optional[bool] = False):
start_strategy: Callable = get_strategy_starter_file(strategy_name)
if strategy_name in STRATEGIES:
if strategy_name in settings.STRATEGIES:
start_strategy(self)
else:
raise NotImplementedError
Expand All @@ -131,7 +136,7 @@ async def start_market_making(self, # type: HummingbotApplication
script_file = global_config_map["script_file_path"].value
folder = dirname(script_file)
if folder == "":
script_file = join(SCRIPTS_PATH, script_file)
script_file = join(settings.SCRIPTS_PATH, script_file)
if self.strategy_name != "pure_market_making":
self._notify("Error: script feature is only available for pure_market_making strategy (for now).")
else:
Expand All @@ -150,3 +155,30 @@ async def start_market_making(self, # type: HummingbotApplication
await self.wait_till_ready(self.kill_switch.start)
except Exception as e:
self.logger().error(str(e), exc_info=True)

async def confirm_oracle_conversion_rate(self, # type: HummingbotApplication
) -> bool:
try:
result = False
self.app.clear_input()
self.placeholder_mode = True
self.app.hide_input = True
for pair in settings.rate_oracle_pairs:
msg = await RateCommand.oracle_rate_msg(pair)
self._notify("\nRate Oracle:\n" + msg)
config = ConfigVar(key="confirm_oracle_use",
type_str="bool",
prompt="Please confirm to proceed if the above oracle source and rates are correct for "
"this strategy (Yes/No) >>> ",
required_if=lambda: True,
validator=lambda v: validate_bool(v))
await self.prompt_a_config(config)
if config.value:
result = True
except OracleRateUnavailable:
self._notify("Oracle rate is not available.")
finally:
self.placeholder_mode = False
self.app.hide_input = False
self.app.change_prompt(prompt=">>> ")
return result
4 changes: 4 additions & 0 deletions hummingbot/client/command/stop_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
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
if TYPE_CHECKING:
from hummingbot.client.hummingbot_application import HummingbotApplication

Expand Down Expand Up @@ -44,6 +45,9 @@ async def stop_loop(self, # type: HummingbotApplication
if self.strategy_task is not None and not self.strategy_task.cancelled():
self.strategy_task.cancel()

if RateOracle.get_instance().started:
RateOracle.get_instance().stop()

if self.markets_recorder is not None:
self.markets_recorder.stop()

Expand Down
4 changes: 2 additions & 2 deletions hummingbot/client/config/global_config_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,11 @@ def global_token_symbol_on_validated(value: str):
default=RateOracleSource.binance.name),
"global_token":
ConfigVar(key="global_token",
prompt="What is your default display token? (e.g. USDT,USD,EUR) >>> ",
prompt="What is your default display token? (e.g. USD,EUR,BTC) >>> ",
type_str="str",
required_if=lambda: False,
on_validated=global_token_on_validated,
default="USDT"),
default="USD"),
"global_token_symbol":
ConfigVar(key="global_token_symbol",
prompt="What is your default display token symbol? (e.g. $,€) >>> ",
Expand Down
4 changes: 4 additions & 0 deletions hummingbot/client/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ class InvalidCommandError(Exception):

class ArgumentParserError(Exception):
pass


class OracleRateUnavailable(Exception):
pass
3 changes: 3 additions & 0 deletions hummingbot/client/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
# Global variables
required_exchanges: List[str] = []
requried_connector_trading_pairs: Dict[str, List[str]] = {}
# Set these two variables if a strategy uses oracle for rate conversion
required_rate_oracle: bool = False
rate_oracle_pairs: List[str] = []

# Global static values
KEYFILE_PREFIX = "key_file_"
Expand Down
3 changes: 2 additions & 1 deletion hummingbot/client/ui/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@ def get_subcommand_completer(self, first_word: str) -> Completer:
@property
def _trading_pair_completer(self) -> Completer:
trading_pair_fetcher = TradingPairFetcher.get_instance()
market = ""
for exchange in sorted(list(CONNECTOR_SETTINGS.keys()), key=len, reverse=True):
if exchange in self.prompt_text:
market = exchange
break
trading_pairs = trading_pair_fetcher.trading_pairs.get(market, []) if trading_pair_fetcher.ready else []
trading_pairs = trading_pair_fetcher.trading_pairs.get(market, []) if trading_pair_fetcher.ready and market else []
return WordCompleter(trading_pairs, ignore_case=True, sentence=True)

@property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ async def _order_book_diff_router(self):

now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug("Diff messages process: %d, rejected: %d, queued: %d",
messages_accepted,
messages_rejected,
messages_queued)
self.logger().debug(f"Diff messages processed: {messages_accepted}, "
f"rejected: {messages_rejected}, queued: {messages_queued}")
messages_accepted = 0
messages_rejected = 0
messages_queued = 0
Expand Down Expand Up @@ -128,14 +126,13 @@ async def _track_single_book(self, trading_pair: str):

now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug("Processed %d order book diffs for %s.",
diff_messages_accepted, trading_pair)
self.logger().debug(f"Processed {diff_messages_accepted} order book diffs for {trading_pair}.")
diff_messages_accepted = 0
last_message_timestamp = now
elif message.type is OrderBookMessageType.SNAPSHOT:
past_diffs: List[OrderBookMessage] = list(past_diffs_window)
order_book.restore_from_snapshot_and_diffs(message, past_diffs)
self.logger().debug("Processed order book snapshot for %s.", trading_pair)
self.logger().debug(f"Processed order book snapshot for {trading_pair}.")
except asyncio.CancelledError:
raise
except Exception:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ async def _track_single_book(self, trading_pair: str):
# Output some statistics periodically.
now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug("Processed %d order book diffs for %s.",
diff_messages_accepted, trading_pair)
self.logger().debug(f"Processed {diff_messages_accepted} order book diffs for {trading_pair}.")
diff_messages_accepted = 0
last_message_timestamp = now
elif message.type is OrderBookMessageType.SNAPSHOT:
Expand All @@ -100,7 +99,7 @@ async def _track_single_book(self, trading_pair: str):
d_bids, d_asks = active_order_tracker.convert_diff_message_to_order_book_row(diff_message)
order_book.apply_diffs(d_bids, d_asks, diff_message.update_id)

self.logger().debug("Processed order book snapshot for %s.", trading_pair)
self.logger().debug(f"Processed order book snapshot for {trading_pair}.")
except asyncio.CancelledError:
raise
except Exception:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,8 @@ async def _order_book_diff_router(self):
# Log some statistics.
now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug("Diff messages processed: %d, rejected: %d, queued: %d",
messages_accepted,
messages_rejected,
messages_queued)
self.logger().debug(f"Diff messages processed: {messages_accepted}, "
f"rejected: {messages_rejected}, queued: {messages_queued}")
messages_accepted = 0
messages_rejected = 0
messages_queued = 0
Expand Down Expand Up @@ -175,7 +173,7 @@ async def _track_single_book(self, trading_pair: str):
s_bids, s_asks = active_order_tracker.convert_snapshot_message_to_order_book_row(message)
order_book.apply_snapshot(s_bids, s_asks, message.update_id)

self.logger().debug("Processed order book snapshot for %s.", trading_pair)
self.logger().debug(f"Processed order book snapshot for {trading_pair}.")
except asyncio.CancelledError:
raise
except Exception:
Expand Down
11 changes: 4 additions & 7 deletions hummingbot/connector/exchange/beaxy/beaxy_order_book_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,8 @@ async def _order_book_diff_router(self):
# Log some statistics.
now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug('Messages processed: %d, rejected: %d, queued: %d',
messages_accepted,
messages_rejected,
messages_queued)
self.logger().debug(f"Messages processed: {messages_accepted}, "
f"rejected: {messages_rejected}, queued: {messages_queued}")
messages_accepted = 0
messages_rejected = 0
messages_queued = 0
Expand Down Expand Up @@ -163,8 +161,7 @@ async def _track_single_book(self, trading_pair: str):
# Output some statistics periodically.
now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug('Processed %d order book diffs for %s.',
diff_messages_accepted, trading_pair)
self.logger().debug(f"Processed {diff_messages_accepted} order book diffs for {trading_pair}.")
diff_messages_accepted = 0
last_message_timestamp = now
elif message.type is OrderBookMessageType.SNAPSHOT:
Expand All @@ -178,7 +175,7 @@ async def _track_single_book(self, trading_pair: str):
d_bids, d_asks = active_order_tracker.convert_diff_message_to_order_book_row(diff_message)
order_book.apply_diffs(d_bids, d_asks, diff_message.update_id)

self.logger().debug('Processed order book snapshot for %s.', trading_pair)
self.logger().debug(f"Processed order book snapshot for {trading_pair}.")
except asyncio.CancelledError:
raise
except Exception:
Expand Down
6 changes: 3 additions & 3 deletions hummingbot/connector/exchange/binance/binance_exchange.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ cdef class BinanceExchange(ExchangeBase):
trading_pairs = list(trading_pairs_to_order_map.keys())
tasks = [self.query_api(self._binance_client.get_my_trades, symbol=convert_to_exchange_trading_pair(trading_pair))
for trading_pair in trading_pairs]
self.logger().debug("Polling for order fills of %d trading pairs.", len(tasks))
self.logger().debug(f"Polling for order fills of {len(tasks)} trading pairs.")
results = await safe_gather(*tasks, return_exceptions=True)
for trades, trading_pair in zip(results, trading_pairs):
order_map = trading_pairs_to_order_map[trading_pair]
Expand Down Expand Up @@ -448,7 +448,7 @@ cdef class BinanceExchange(ExchangeBase):
trading_pairs = self._order_book_tracker._trading_pairs
tasks = [self.query_api(self._binance_client.get_my_trades, symbol=convert_to_exchange_trading_pair(trading_pair))
for trading_pair in trading_pairs]
self.logger().debug("Polling for order fills of %d trading pairs.", len(tasks))
self.logger().debug(f"Polling for order fills of {len(tasks)} trading pairs.")
exchange_history = await safe_gather(*tasks, return_exceptions=True)
for trades, trading_pair in zip(exchange_history, trading_pairs):
if isinstance(trades, Exception):
Expand Down Expand Up @@ -494,7 +494,7 @@ cdef class BinanceExchange(ExchangeBase):
tasks = [self.query_api(self._binance_client.get_order,
symbol=convert_to_exchange_trading_pair(o.trading_pair), origClientOrderId=o.client_order_id)
for o in tracked_orders]
self.logger().debug("Polling for order status updates of %d orders.", len(tasks))
self.logger().debug(f"Polling for order status updates of {len(tasks)} orders.")
results = await safe_gather(*tasks, return_exceptions=True)
for order_update, tracked_order in zip(results, tracked_orders):
client_order_id = tracked_order.client_order_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,8 @@ async def _order_book_diff_router(self):
# Log some statistics.
now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug("Diff messages processed: %d, rejected: %d, queued: %d",
messages_accepted,
messages_rejected,
messages_queued)
self.logger().debug(f"Diff messages processed: {messages_accepted}, "
f"rejected: {messages_rejected}, queued: {messages_queued}")
messages_accepted = 0
messages_rejected = 0
messages_queued = 0
Expand Down Expand Up @@ -129,14 +127,13 @@ async def _track_single_book(self, trading_pair: str):
# Output some statistics periodically.
now: float = time.time()
if int(now / 60.0) > int(last_message_timestamp / 60.0):
self.logger().debug("Processed %d order book diffs for %s.",
diff_messages_accepted, trading_pair)
self.logger().debug(f"Processed {diff_messages_accepted} order book diffs for {trading_pair}.")
diff_messages_accepted = 0
last_message_timestamp = now
elif message.type is OrderBookMessageType.SNAPSHOT:
past_diffs: List[OrderBookMessage] = list(past_diffs_window)
order_book.restore_from_snapshot_and_diffs(message, past_diffs)
self.logger().debug("Processed order book snapshot for %s.", trading_pair)
self.logger().debug(f"Processed order book snapshot for {trading_pair}.")
except asyncio.CancelledError:
raise
except Exception:
Expand Down
Loading

0 comments on commit 84f175d

Please sign in to comment.