Skip to content

Commit

Permalink
Merge pull request #1058 from Drakkar-Software/dev
Browse files Browse the repository at this point in the history
Master merge
  • Loading branch information
GuillaumeDSM authored Sep 25, 2023
2 parents 2eb6835 + 8dfadf0 commit 75099b9
Show file tree
Hide file tree
Showing 23 changed files with 185 additions and 104 deletions.
2 changes: 2 additions & 0 deletions Services/Interfaces/web_interface/controllers/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def home():
all_time_frames = models.get_all_watched_time_frames()
display_time_frame = models.get_display_timeframe()
display_orders = models.get_display_orders()
sandbox_exchanges = models.get_sandbox_exchanges()
try:
user_id = models.get_user_account_id()
display_feedback_form = form_to_display and not models.has_filled_form(form_to_display)
Expand All @@ -63,6 +64,7 @@ def home():
user_id=user_id,
form_to_display=form_to_display,
display_feedback_form=display_feedback_form,
sandbox_exchanges=sandbox_exchanges
)
else:
return flask.redirect(flask.url_for("terms"))
2 changes: 2 additions & 0 deletions Services/Interfaces/web_interface/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
reload_tentacle_config,
update_config_currencies,
get_config_required_candles_count,
get_sandbox_exchanges,
)
from tentacles.Services.Interfaces.web_interface.models.dashboard import (
parse_get_symbol,
Expand Down Expand Up @@ -449,5 +450,6 @@
"reload_tentacle_config",
"update_config_currencies",
"get_config_required_candles_count",
"get_sandbox_exchanges",
"WebInterfaceTab",
]
8 changes: 8 additions & 0 deletions Services/Interfaces/web_interface/models/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,14 @@ def get_current_exchange():
return DEFAULT_EXCHANGE


def get_sandbox_exchanges() -> list:
return [
trading_api.get_exchange_name(exchange_manager)
for exchange_manager in interfaces_util.get_exchange_managers()
if trading_api.get_exchange_manager_is_sandboxed(exchange_manager)
]


def change_reference_market_on_config_currencies(old_base_currency: str, new_quote_currency: str) -> bool:
"""
Change the base currency from old to new for all configured pair
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ _TUTORIALS = {
},
{
title: 'See also',
intro: `More details on ${getWebsiteLink("/guides/#trading_modes", "the OctoBot guide")}.`
intro: `More details on ${getDocsLink("/configuration/profiles", "the profiles guide")}.`
},
]
}
Expand Down Expand Up @@ -242,7 +242,7 @@ _TUTORIALS = {
},
{
title: 'See also',
intro: `More details the ${getWebsiteLink("/guides/#backtesting", "backtesting guide")}.`
intro: `More details the ${getDocsLink("/advanced_usage/backtesting-and-strategy-optimization", "backtesting guide")}.`
},
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ <h2>Test configuration
<span class="badge badge-warning text-center waves-effect">Activation required <i class="fas fa-sync-alt"></i></span>
</a>
{% endif %}
<a class="float-right blue-text" target="_blank" rel="noopener" href="{{OCTOBOT_WEBSITE_URL}}/guides/#backtesting">
<a class="float-right blue-text" target="_blank" rel="noopener" href="{{OCTOBOT_DOCS_URL}}/advanced_usage/backtesting-and-strategy-optimization">
&nbsp <i class="fa-solid fa-question"></i>
</a>
{% if activated_trading_mode %}
Expand Down
16 changes: 14 additions & 2 deletions Services/Interfaces/web_interface/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,20 @@ <h4>
<div class="card">
<div class="card-header d-flex justify-content-between" id="all-watched-markets">
<div>

</div><h4>Watched markets</h4>
{% if sandbox_exchanges %}
<h5>
<span class="badge badge-warning"
id="sandbox-badge"
data-toggle="tooltip"
title="{{ sandbox_exchanges | join(' and ')}} testnet / sandbox is enabled. This means that
your OctoBot is trading using testnet prices and accounts, which
might not be representative of real markets. Use the trading simulator to test a trading
strategy in real conditions."
>{{sandbox_exchanges[0] | capitalize}} sandbox</span>
</h5>
{% endif %}
</div>
<h4>Watched markets</h4>
<div>
<a class="waves-effect float-right" href="#" id="display-dashboard-settings-modal-label"
data-toggle="modal" data-target="#dashboard-settings-modal">
Expand Down
5 changes: 2 additions & 3 deletions Services/Interfaces/web_interface/templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,7 @@ <h4>This trading mode doesn't need any strategy.</h4>
<h2>
Profile strategy configuration
<span href="" class="badge badge-dark waves-effect align-top" data-role="current-profile-selector">read only</span>
<a class="float-right blue-text" target="_blank" rel="noopener"
href="{{OCTOBOT_WEBSITE_URL}}/guides/#trading_modes">
<a class="float-right blue-text" data-intro="profile">
<i class="fa-solid fa-question"></i>
</a>
</h2>
Expand Down Expand Up @@ -359,7 +358,7 @@ <h4 class="alert-heading">Strategies</h4>
<div class="card-header">
<h2>Exchanges
<a class="float-right blue-text" target="_blank" rel="noopener"
href="{{OCTOBOT_WEBSITE_URL}}/guides/#exchanges">
href="{{EXCHANGES_DOCS_URL}}">
<i class="fa-solid fa-question"></i>
</a>
</h2>
Expand Down
2 changes: 1 addition & 1 deletion Services/Interfaces/web_interface/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,4 @@ async def stop(self):
self.websocket_instance.stop()
self.logger.debug("Stopped web interface")
except Exception as e:
self.logger.exception(f"Error when stopping web interface : {e}", False)
self.logger.exception(e, False, f"Error when stopping web interface : {e}")
4 changes: 2 additions & 2 deletions Services/Services_bases/telegram_service/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class TelegramService(services.AbstractService):
CONNECT_TIMEOUT = 7 # default is 5, use 7 to take slow connections into account
CHAT_ID = "chat-id"
LOGGERS = ["telegram._bot", "telegram.ext.Updater", "telegram.ext.ExtBot",
"hpack.hpack", "hpack.table", "httpx._client"]
"hpack.hpack", "hpack.table"]

def __init__(self):
super().__init__()
Expand Down Expand Up @@ -67,7 +67,7 @@ def get_read_only_info(self):

@classmethod
def get_help_page(cls) -> str:
return f"{constants.OCTOBOT_WEBSITE_URL}/guides/#telegram"
return f"{constants.OCTOBOT_DOCS_URL}/interfaces/telegram-interface"

@staticmethod
def is_setup_correctly(config):
Expand Down
2 changes: 1 addition & 1 deletion Services/Services_bases/web_service/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def get_required_config(self):

@classmethod
def get_help_page(cls) -> str:
return f"{constants.OCTOBOT_WEBSITE_URL}/#octobot"
return f"{constants.OCTOBOT_DOCS_URL}/interfaces/web-interface"

@staticmethod
def is_setup_correctly(config):
Expand Down
3 changes: 2 additions & 1 deletion Trading/Exchange/ascendex/ascendex_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@ class AscendexCCXTAdapter(exchanges.CCXTAdapter):

def fix_ticker(self, raw, **kwargs):
fixed = super().fix_ticker(raw, **kwargs)
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = self.connector.client.milliseconds()
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = \
fixed.get(trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value) or self.connector.client.seconds()
return fixed
3 changes: 0 additions & 3 deletions Trading/Exchange/binance/binance_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,6 @@ def fix_trades(self, raw, **kwargs):
raw = super().fix_trades(raw, **kwargs)
for trade in raw:
trade[trading_enums.ExchangeConstantsOrderColumns.STATUS.value] = trading_enums.OrderStatus.CLOSED.value
trade[trading_enums.ExchangeConstantsOrderColumns.EXCHANGE_ID.value] = trade[
trading_enums.ExchangeConstantsOrderColumns.ORDER.value
]
return raw

def parse_position(self, fixed, force_empty=False, **kwargs):
Expand Down
3 changes: 2 additions & 1 deletion Trading/Exchange/bybit/bybit_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,8 @@ def _adapt_order_type(self, fixed):

def fix_ticker(self, raw, **kwargs):
fixed = super().fix_ticker(raw, **kwargs)
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = self.connector.client.milliseconds()
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = \
fixed.get(trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value) or self.connector.client.seconds()
return fixed

def parse_position(self, fixed, **kwargs):
Expand Down
3 changes: 0 additions & 3 deletions Trading/Exchange/coinbase/coinbase_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ def fix_trades(self, raw, **kwargs):
raw = super().fix_trades(raw, **kwargs)
for trade in raw:
trade[trading_enums.ExchangeConstantsOrderColumns.STATUS.value] = trading_enums.OrderStatus.CLOSED.value
trade[trading_enums.ExchangeConstantsOrderColumns.EXCHANGE_ID.value] = trade[
trading_enums.ExchangeConstantsOrderColumns.ORDER.value
]
try:
if trade[trading_enums.ExchangeConstantsOrderColumns.AMOUNT.value] is None and \
trade[trading_enums.ExchangeConstantsOrderColumns.COST.value] and \
Expand Down
3 changes: 0 additions & 3 deletions Trading/Exchange/coinbase_pro/coinbase_pro_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,4 @@ def fix_trades(self, raw, **kwargs):
raw = super().fix_trades(raw, **kwargs)
for trade in raw:
trade[trading_enums.ExchangeConstantsOrderColumns.STATUS.value] = trading_enums.OrderStatus.CLOSED.value
trade[trading_enums.ExchangeConstantsOrderColumns.EXCHANGE_ID.value] = trade[
trading_enums.ExchangeConstantsOrderColumns.ORDER.value
]
return raw
3 changes: 2 additions & 1 deletion Trading/Exchange/gateio/gateio_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ class GateioCCXTAdapter(exchanges.CCXTAdapter):

def fix_ticker(self, raw, **kwargs):
fixed = super().fix_ticker(raw, **kwargs)
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = self.connector.client.milliseconds()
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = \
fixed.get(trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value) or self.connector.client.seconds()
return fixed
7 changes: 4 additions & 3 deletions Trading/Exchange/kucoin/kucoin_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ async def get_account_id(self, **kwargs: dict) -> str:
account_id = None
subaccount_id = None
sub_accounts = await self.connector.client.private_get_sub_accounts()
has_subaccounts = bool(sub_accounts["data"])
for account in sub_accounts["data"]:
if account["subUserId"]:
subaccount_id = account["subName"]
Expand All @@ -147,22 +148,22 @@ async def get_account_id(self, **kwargs: dict) -> str:
f"sub_accounts={sub_accounts} subaccount_api_key_details={subaccount_api_key_details}"
)
return constants.DEFAULT_ACCOUNT_ID
if account_id is None:
if has_subaccounts and account_id is None:
self.logger.error(
f"kucoin api changed: can't fetch master account account_id. "
f"kucoin get_account_id has to be updated."
f"sub_accounts={sub_accounts}"
)
account_id = constants.DEFAULT_ACCOUNT_ID
# we are on the master account
return account_id
return account_id or constants.DEFAULT_ACCOUNT_ID
except ccxt.AuthenticationError:
# when api key is wrong
raise
except ccxt.ExchangeError:
# ExchangeError('kucoin This user is not a master user')
# raised when calling this endpoint with a subaccount
return constants.DEFAULT_ACCOUNT_ID
return constants.DEFAULT_SUBACCOUNT_ID


@_kucoin_retrier
Expand Down
3 changes: 2 additions & 1 deletion Trading/Exchange/wavesexchange/wavesexchange_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ class WavesCCXTAdapter(exchanges.CCXTAdapter):

def fix_ticker(self, raw, **kwargs):
fixed = super().fix_ticker(raw, **kwargs)
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = self.connector.client.milliseconds()
fixed[trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value] = \
fixed.get(trading_enums.ExchangeConstantsTickersColumns.TIMESTAMP.value) or self.connector.client.seconds()
return fixed
18 changes: 9 additions & 9 deletions Trading/Mode/dca_trading_mode/dca_trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,19 @@ async def _create_entry_with_chained_exit_orders(self, entry_order, entry_price,
)
stop_price = entry_price * (
trading_constants.ONE - (
self.trading_mode.stop_loss_price_multiplier * exit_multiplier_side_flag
)
self.trading_mode.stop_loss_price_multiplier * exit_multiplier_side_flag
)
)
first_sell_price = entry_price * (
trading_constants.ONE + (
self.trading_mode.exit_limit_orders_price_multiplier * exit_multiplier_side_flag
)
self.trading_mode.exit_limit_orders_price_multiplier * exit_multiplier_side_flag
)
)
last_sell_price = entry_price * (
trading_constants.ONE + (
self.trading_mode.secondary_exit_orders_price_multiplier *
(1 + self.trading_mode.secondary_exit_orders_count) * exit_multiplier_side_flag
)
self.trading_mode.secondary_exit_orders_price_multiplier *
(1 + self.trading_mode.secondary_exit_orders_count) * exit_multiplier_side_flag
)
)
# split entry into multiple exits if necessary (and possible)
exit_quantities = self._split_entry_quantity(
Expand Down Expand Up @@ -264,7 +264,7 @@ async def _create_entry_with_chained_exit_orders(self, entry_order, entry_price,
if i == 1 else (
self.trading_mode.exit_limit_orders_price_multiplier +
self.trading_mode.secondary_exit_orders_price_multiplier * i
)
)
take_profit_price = trading_personal_data.decimal_adapt_price(
symbol_market,
entry_price * (
Expand All @@ -291,7 +291,7 @@ def _split_entry_quantity(quantity, target_exits_count, lowest_price, highest_pr
if target_exits_count == 1:
return [(1, quantity)]
adapted_sell_orders_count, increment = trading_personal_data.get_split_orders_count_and_increment(
lowest_price, highest_price, quantity, target_exits_count, symbol_market
lowest_price, highest_price, quantity, target_exits_count, symbol_market, False
)
if adapted_sell_orders_count:
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ def _generate_sell_orders(self, sell_orders_count, quantity, sell_weight, sell_b
volume_with_price = []
sell_max = sell_base * self.PRICE_WEIGH_TO_PRICE_PERCENT[sell_weight]
adapted_sell_orders_count, increment = trading_personal_data.get_split_orders_count_and_increment(
sell_base, sell_max, quantity, sell_orders_count, symbol_market
sell_base, sell_max, quantity, sell_orders_count, symbol_market, True
)
if adapted_sell_orders_count:
order_volume = quantity / adapted_sell_orders_count
Expand Down
4 changes: 4 additions & 0 deletions Trading/Mode/grid_trading_mode/grid_trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ async def _generate_staggered_orders(self, current_price, ignore_available_funds
missing_orders, state, _ = self._analyse_current_orders_situation(
sorted_orders, recently_closed_trades, lowest_buy, highest_sell, current_price
)
if missing_orders:
self.logger.info(
f"{len(missing_orders)} missing {self.symbol} orders on {self.exchange_name}: {missing_orders}"
)
await self._handle_missed_mirror_orders_fills(recently_closed_trades, missing_orders, current_price)
try:
buy_orders = self._create_orders(lowest_buy, highest_buy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,20 @@ def get_open_order_from_description(self, order_descriptions, symbol):
found_orders += accurate_orders
continue
# 2nd chance: use order type and price as these are kept between bot restarts (loaded from exchange)
found_orders += [
orders = [
(order_description, order)
for order in self.exchange_manager.exchange_personal_data.orders_manager.get_open_orders(symbol=symbol)
if (order.origin_price == order_description[trading_enums.TradingSignalOrdersAttrs.STOP_PRICE.value] or
order.origin_price == order_description[trading_enums.TradingSignalOrdersAttrs.LIMIT_PRICE.value])
and self._is_compatible_order_type(order, order_description)
]
if orders:
found_orders += orders
else:
self.logger.error(
f"Order not found on {self.exchange_manager.exchange_name} open orders, "
f"description: {order_description}"
)
return found_orders

def _is_compatible_order_type(self, order, order_description):
Expand Down
Loading

0 comments on commit 75099b9

Please sign in to comment.