Skip to content

Commit

Permalink
Merge pull request hummingbot#3123 from CoinAlpha/feat/oracle_on_arb_…
Browse files Browse the repository at this point in the history
…xemm

[v0.38] feat/ oracle on Arbitrage and XEMM strategies
  • Loading branch information
dennisocana authored Apr 2, 2021
2 parents f4d99f1 + c9d5c13 commit d87ec66
Show file tree
Hide file tree
Showing 22 changed files with 479 additions and 86 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
Loading

0 comments on commit d87ec66

Please sign in to comment.