[go: up one dir, main page]

Menu

[2b4197]: / octobot / limits.py  Maximize  Restore  History

Download this file

184 lines (166 with data), 9.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
# Copyright (c) 2023 Drakkar-Software, All rights reserved.
#
# OctoBot is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# OctoBot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
import time
import math
import octobot.constants as constants
import octobot_commons.constants as common_constants
import octobot_commons.enums as common_enums
import octobot_commons.logging as logging
import octobot_commons.time_frame_manager as time_frame_manager
import octobot_tentacles_manager.api as tentacles_manager_api
import octobot_evaluators.api as evaluators_api
import octobot_trading.api as trading_api
class ReachedLimitError(Exception):
pass
def _apply_exchanges_limits(dict_config, logger, limit):
if len(trading_api.get_enabled_exchanges_names(dict_config)) > limit:
enabled_exchanges = []
for exchange, config in dict_config.get(common_constants.CONFIG_EXCHANGES, {}).items():
if config.get(common_constants.CONFIG_ENABLED_OPTION, True):
if len(enabled_exchanges) < limit:
enabled_exchanges.append(exchange)
else:
config[common_constants.CONFIG_ENABLED_OPTION] = False
logger.error("Disabled " + exchange)
return f"Reached maximum allowed simultaneous exchanges for this plan, maximum is {limit}. " \
f"Your OctoBot will trade on the following exchanges: {', '.join(enabled_exchanges)}"
return ""
def _apply_symbols_limits(dict_config, logger, limit):
enabled_symbols = []
has_disabled_symbols = False
message = ""
for currency, crypto_currency_data in dict_config.get(common_constants.CONFIG_CRYPTO_CURRENCIES, {}).items():
if crypto_currency_data.get(common_constants.CONFIG_ENABLED_OPTION, True):
if len(enabled_symbols) >= limit:
crypto_currency_data[common_constants.CONFIG_ENABLED_OPTION] = False
logger.error(f"Disabled all {currency} trading pairs")
has_disabled_symbols = True
continue
updated_symbols = []
for symbol in crypto_currency_data.get(common_constants.CONFIG_CRYPTO_PAIRS, []):
if symbol == common_constants.CONFIG_SYMBOLS_WILDCARD[0] \
or symbol == common_constants.CONFIG_SYMBOLS_WILDCARD:
crypto_currency_data[common_constants.CONFIG_ENABLED_OPTION] = False
message = f"Disabled wildcard symbol for {currency}. "
has_disabled_symbols = True
break
else:
if len(enabled_symbols) < limit:
enabled_symbols.append(symbol)
updated_symbols.append(symbol)
else:
has_disabled_symbols = True
logger.error(f"Disabled {symbol} trading pair from {currency}")
crypto_currency_data[common_constants.CONFIG_CRYPTO_PAIRS] = updated_symbols
if has_disabled_symbols:
return f"{message}Reached maximum allowed simultaneous trading pairs for this plan, maximum is {limit}. " \
f"Your OctoBot will trade following pairs: {', '.join(enabled_symbols)}."
return message
def _apply_time_frames_limits(full_config, logger, limit):
tentacles_setup_config = tentacles_manager_api.get_tentacles_setup_config(full_config.get_tentacles_config_path())
has_disabled_time_frames = False
all_enabled_time_frames = []
# patch time frames config
for strategy_class in evaluators_api.get_activated_strategies_classes(tentacles_setup_config):
config_time_frames = evaluators_api.get_time_frames_from_strategy(
strategy_class, full_config.config, tentacles_setup_config
)
combined_time_frames = set(all_enabled_time_frames + config_time_frames)
if len(combined_time_frames) < limit:
all_enabled_time_frames = time_frame_manager.sort_time_frames(list(combined_time_frames))
elif len(combined_time_frames) > limit:
has_disabled_time_frames = True
if len(all_enabled_time_frames) == limit:
# no timeframe to add
pass
else:
# disable shortest timeframes first
missing_tf = time_frame_manager.sort_time_frames([
tf
for tf in config_time_frames
if tf not in all_enabled_time_frames
])
added_time_frames = missing_tf[limit-len(all_enabled_time_frames):]
all_enabled_time_frames = time_frame_manager.sort_time_frames(
list(all_enabled_time_frames) + added_time_frames
)
else:
all_enabled_time_frames = list(combined_time_frames)
if has_disabled_time_frames:
should_update_config = False
strategy_enabled_time_frames = [
tf
for tf in config_time_frames
if tf in all_enabled_time_frames
]
for time_frame in config_time_frames:
if time_frame not in strategy_enabled_time_frames:
should_update_config = True
logger.error(f"Disabled {time_frame.value} time frame for {strategy_class.get_name()}")
if should_update_config:
evaluators_api.update_time_frames_config(
strategy_class, tentacles_setup_config, strategy_enabled_time_frames
)
if has_disabled_time_frames:
return f"Reached maximum allowed simultaneous time frames for this plan, maximum is {limit}. " \
f"Your OctoBot will trade using following time frames: " \
f"{', '.join([tf.value for tf in all_enabled_time_frames])}."
return ""
def apply_config_limits(configuration) -> list:
logger = logging.get_logger("ConfigurationLimits")
limit_warning_messages = []
try:
if constants.MAX_ALLOWED_EXCHANGES != constants.UNLIMITED_ALLOWED:
if message := _apply_exchanges_limits(configuration.config, logger, constants.MAX_ALLOWED_EXCHANGES):
limit_warning_messages.append(message)
if constants.MAX_ALLOWED_SYMBOLS != constants.UNLIMITED_ALLOWED:
if message := _apply_symbols_limits(configuration.config, logger, constants.MAX_ALLOWED_SYMBOLS):
limit_warning_messages.append(message)
if constants.MAX_ALLOWED_TIME_FRAMES != constants.UNLIMITED_ALLOWED:
if message := _apply_time_frames_limits(configuration, logger, constants.MAX_ALLOWED_TIME_FRAMES):
limit_warning_messages.append(message)
except Exception as err:
logger.exception(err, True, f"Error when applying limits: {err}")
if limit_warning_messages:
for message in limit_warning_messages:
logger.error(message)
return limit_warning_messages
def _check_max_backtesting_setting(setting_name, limit, values):
if values and limit != constants.UNLIMITED_ALLOWED and len(values) > limit:
raise ReachedLimitError(
f"The maximum allowed simultaneous backtesting {setting_name} for your selected plan is {limit}"
)
def _check_max_backtesting_candles_count(time_frames, start_timestamp, end_timestamp):
time_frames = time_frames or [tf for tf in common_enums.TimeFrames]
_check_max_backtesting_setting("time frames using custom duration", constants.MAX_ALLOWED_TIME_FRAMES, time_frames)
if start_timestamp is None or constants.MAX_ALLOWED_BACKTESTING_CANDLES_HISTORY == constants.UNLIMITED_ALLOWED:
return
if end_timestamp is None:
end_timestamp = time.time()
shortest_time_frame = time_frame_manager.sort_time_frames(time_frames)[0]
time_frame_seconds = common_enums.TimeFramesMinutes[shortest_time_frame] * common_constants.MINUTE_TO_SECONDS
candles_count = math.floor((end_timestamp - start_timestamp) / time_frame_seconds)
if candles_count > constants.MAX_ALLOWED_BACKTESTING_CANDLES_HISTORY:
raise ReachedLimitError(
f"For this plan, the maximum allowed backtesting candles per time frame "
f"is {constants.MAX_ALLOWED_BACKTESTING_CANDLES_HISTORY}. "
f"With the selected backtesting duration, the {shortest_time_frame.value} time frame would "
f"cover {candles_count} candles. Please select other time frames or reduce the backtesting duration."
)
def ensure_backtesting_limits(exchanges, symbols, time_frames, start_timestamp, end_timestamp) -> None:
_check_max_backtesting_setting("exchanges", constants.MAX_ALLOWED_EXCHANGES, exchanges)
_check_max_backtesting_setting("trading pairs", constants.MAX_ALLOWED_SYMBOLS, symbols)
_check_max_backtesting_candles_count(time_frames, start_timestamp, end_timestamp)