Skip to content

Commit 8fbe596

Browse files
authored
Merge pull request #1659 from Drakkar-Software/dev
Master update
2 parents 89122fb + 2d06100 commit 8fbe596

File tree

39 files changed

+278
-113
lines changed

39 files changed

+278
-113
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
git clone -q $OCTOBOT_GH_REPO -b ${TARGET_BRANCH} || git clone -q $OCTOBOT_GH_REPO -b $OCTOBOT_DEFAULT_BRANCH
3939
cd OctoBot
4040
git status
41-
pip install --prefer-binary -r dev_requirements.txt -r requirements.txt
41+
pip install --prefer-binary -r dev_requirements.txt -r requirements.txt -r full_requirements.txt
4242
cd ..
4343
mkdir new_tentacles
4444
cp -r Automation Backtesting Evaluator Meta Services Trading profiles new_tentacles
@@ -70,7 +70,7 @@ jobs:
7070
cd OctoBot
7171
git status
7272
pip install --upgrade pip setuptools wheel
73-
pip install --prefer-binary -r dev_requirements.txt -r requirements.txt
73+
pip install --prefer-binary -r dev_requirements.txt -r requirements.txt -r full_requirements.txt
7474
cd ..
7575
mkdir new_tentacles
7676
xcopy Automation new_tentacles\\Automation /E/H/I
@@ -134,7 +134,7 @@ jobs:
134134
git clone -q $OCTOBOT_GH_REPO -b ${TARGET_BRANCH} || git clone -q $OCTOBOT_GH_REPO -b $OCTOBOT_DEFAULT_BRANCH
135135
cd OctoBot
136136
git status
137-
pip install --prefer-binary -r dev_requirements.txt -r requirements.txt
137+
pip install --prefer-binary -r dev_requirements.txt -r requirements.txt -r full_requirements.txt
138138
cd ..
139139
mkdir new_tentacles
140140
cp -r Automation Backtesting Evaluator Meta Services Trading profiles new_tentacles

Evaluator/Social/forum_evaluator/forum.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
# RedditForumEvaluator is used to get an overall state of a market, it will not trigger a trade
3131
# (notify its evaluators) but is used to measure hype and trend of a market.
3232
class RedditForumEvaluator(evaluators.SocialEvaluator):
33-
34-
SERVICE_FEED_CLASS = Services_feeds.RedditServiceFeed
33+
SERVICE_FEED_CLASS = Services_feeds.RedditServiceFeed if hasattr(Services_feeds, 'RedditServiceFeed') else None
3534

3635
def __init__(self, tentacles_setup_config):
3736
evaluators.SocialEvaluator.__init__(self, tentacles_setup_config)

Evaluator/Social/news_evaluator/news.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# disable inheritance to disable tentacle visibility. Disabled as starting from feb 9 2023, API is now paid only
2828
# class TwitterNewsEvaluator(evaluators.SocialEvaluator):
2929
class TwitterNewsEvaluator:
30-
SERVICE_FEED_CLASS = Services_feeds.TwitterServiceFeed
30+
SERVICE_FEED_CLASS = Services_feeds.TwitterServiceFeed if hasattr(Services_feeds, 'TwitterServiceFeed') else None
3131

3232
# max time to live for a pulse is 10min
3333
_EVAL_MAX_TIME_TO_LIVE = 10 * commons_constants.MINUTE_TO_SECONDS

Evaluator/Social/signal_evaluator/signal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
class TelegramSignalEvaluator(evaluators.SocialEvaluator):
26-
SERVICE_FEED_CLASS = Services_feeds.TelegramServiceFeed
26+
SERVICE_FEED_CLASS = Services_feeds.TelegramServiceFeed if hasattr(Services_feeds, 'TelegramServiceFeed') else None
2727

2828
def init_user_inputs(self, inputs: dict) -> None:
2929
channels_config = self.UI.user_input(services_constants.CONFIG_TELEGRAM_CHANNEL,
@@ -106,7 +106,7 @@ def _get_tentacle_registration_topic(self, all_symbols_by_crypto_currencies, tim
106106

107107

108108
class TelegramChannelSignalEvaluator(evaluators.SocialEvaluator):
109-
SERVICE_FEED_CLASS = Services_feeds.TelegramApiServiceFeed
109+
SERVICE_FEED_CLASS = Services_feeds.TelegramApiServiceFeed if hasattr(Services_feeds, 'TelegramApiServiceFeed') else None
110110

111111
SIGNAL_PATTERN_KEY = "signal_pattern"
112112
SIGNAL_PATTERN_MARKET_BUY_KEY = "MARKET_BUY"

Evaluator/Social/trends_evaluator/trends.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626

2727
class GoogleTrendsEvaluator(evaluators.SocialEvaluator):
28-
SERVICE_FEED_CLASS = Services_feeds.GoogleServiceFeed
28+
SERVICE_FEED_CLASS = Services_feeds.GoogleServiceFeed if hasattr(Services_feeds, 'GoogleServiceFeed') else None
2929

3030
def __init__(self, tentacles_setup_config):
3131
evaluators.SocialEvaluator.__init__(self, tentacles_setup_config)

Evaluator/TA/ai_evaluator/ai.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@
2727
import octobot_trading.api as trading_api
2828
import octobot_services.api as services_api
2929
import octobot_services.errors as services_errors
30-
import tentacles.Services.Services_bases.gpt_service as gpt_service
30+
import tentacles.Services.Services_bases
31+
32+
33+
def _get_gpt_service():
34+
try:
35+
return tentacles.Services.Services_bases.GPTService
36+
except (AttributeError, ImportError):
37+
raise ImportError("the gpt_service tentacle is not installed")
3138

3239

3340
class GPTEvaluator(evaluators.TAEvaluator):
@@ -58,7 +65,7 @@ def __init__(self, tentacles_setup_config):
5865
self.source = None
5966
self.period = None
6067
self.min_confidence_threshold = 100
61-
self.gpt_model = gpt_service.GPTService.DEFAULT_MODEL
68+
self.gpt_model = _get_gpt_service().DEFAULT_MODEL
6269
self.is_backtesting = False
6370
self.min_allowed_timeframe = os.getenv("MIN_GPT_TIMEFRAME", None)
6471
self.enable_model_selector = os_util.parse_boolean_environment_var("ENABLE_GPT_MODELS_SELECTOR", "True")
@@ -70,7 +77,7 @@ def __init__(self, tentacles_setup_config):
7077
except ValueError:
7178
self.logger.error(f"Invalid timeframe configuration: unknown timeframe: '{self.min_allowed_timeframe}'")
7279
self.allow_reevaluations = os_util.parse_boolean_environment_var(self.ALLOW_GPT_REEVALUATION_ENV, "True")
73-
self.gpt_tokens_limit = gpt_service.GPTService.NO_TOKEN_LIMIT_VALUE
80+
self.gpt_tokens_limit = _get_gpt_service().NO_TOKEN_LIMIT_VALUE
7481
self.services_config = None
7582

7683
def enable_reevaluation(self) -> bool:
@@ -93,7 +100,7 @@ async def load_and_save_user_inputs(self, bot_id: str) -> dict:
93100
:return: the filled user input configuration
94101
"""
95102
self.is_backtesting = self._is_in_backtesting()
96-
if self.is_backtesting and not gpt_service.GPTService.BACKTESTING_ENABLED:
103+
if self.is_backtesting and not _get_gpt_service().BACKTESTING_ENABLED:
97104
self.logger.error(f"{self.get_name()} is disabled in backtesting. It will only emit neutral evaluations")
98105
await self._init_GPT_models()
99106
return await super().load_and_save_user_inputs(bot_id)
@@ -122,10 +129,10 @@ def init_user_inputs(self, inputs: dict) -> None:
122129
if self.enable_model_selector:
123130
current_value = self.specific_config.get("GPT_model")
124131
models = list(self.GPT_MODELS) or (
125-
[current_value] if current_value else [gpt_service.GPTService.DEFAULT_MODEL]
132+
[current_value] if current_value else [_get_gpt_service().DEFAULT_MODEL]
126133
)
127134
self.gpt_model = self.UI.user_input(
128-
"GPT model", enums.UserInputTypes.OPTIONS, gpt_service.GPTService.DEFAULT_MODEL,
135+
"GPT model", enums.UserInputTypes.OPTIONS, _get_gpt_service().DEFAULT_MODEL,
129136
inputs, options=sorted(models),
130137
title="GPT Model: the GPT model to use. Enable the evaluator to load other models."
131138
)
@@ -140,18 +147,18 @@ def init_user_inputs(self, inputs: dict) -> None:
140147
if self.ALLOW_TOKEN_LIMIT_UPDATE:
141148
self.gpt_tokens_limit = self.UI.user_input(
142149
"max_gpt_tokens", enums.UserInputTypes.INT,
143-
self.gpt_tokens_limit, inputs, min_val=gpt_service.GPTService.NO_TOKEN_LIMIT_VALUE,
150+
self.gpt_tokens_limit, inputs, min_val=_get_gpt_service().NO_TOKEN_LIMIT_VALUE,
144151
title=f"OpenAI token limit: maximum daily number of tokens to consume with a given OctoBot instance. "
145-
f"Use {gpt_service.GPTService.NO_TOKEN_LIMIT_VALUE} to remove the limit."
152+
f"Use {_get_gpt_service().NO_TOKEN_LIMIT_VALUE} to remove the limit."
146153
)
147154

148155
async def _init_GPT_models(self):
149156
if not self.GPT_MODELS:
150-
self.GPT_MODELS = [gpt_service.GPTService.DEFAULT_MODEL]
157+
self.GPT_MODELS = [_get_gpt_service().DEFAULT_MODEL]
151158
if self.enable_model_selector and not self.is_backtesting:
152159
try:
153160
service = await services_api.get_service(
154-
gpt_service.GPTService, self.is_backtesting, self.services_config
161+
_get_gpt_service(), self.is_backtesting, self.services_config
155162
)
156163
self.GPT_MODELS = service.models
157164
self.ALLOW_TOKEN_LIMIT_UPDATE = service.allow_token_limit_update()
@@ -226,7 +233,7 @@ def get_formatted_data(self, computed_data) -> str:
226233
async def ask_gpt(self, preprompt, inputs, symbol, time_frame, candle_time) -> str:
227234
try:
228235
service = await services_api.get_service(
229-
gpt_service.GPTService,
236+
_get_gpt_service(),
230237
self.is_backtesting,
231238
{} if self.is_backtesting else self.services_config
232239
)

Evaluator/Util/text_analysis/text_analysis.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@
1414
# You should have received a copy of the GNU Lesser General Public
1515
# License along with this library.
1616

17-
import vaderSentiment.vaderSentiment as vaderSentiment
17+
import octobot_commons.constants as commons_constants
18+
try:
19+
import vaderSentiment.vaderSentiment as vaderSentiment
20+
except ImportError:
21+
if commons_constants.USE_MINIMAL_LIBS:
22+
# mock vaderSentiment imports
23+
class VaderSentimentImportMock:
24+
class SentimentIntensityAnalyzer:
25+
def __init__(self, *args):
26+
raise ImportError("vaderSentiment not installed")
27+
vaderSentiment = VaderSentimentImportMock()
1828

1929

2030
class TextAnalysis:
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Drakkar-Software OctoBot-Commons
2+
# Copyright (c) Drakkar-Software, All rights reserved.
3+
#
4+
# This library is free software; you can redistribute it and/or
5+
# modify it under the terms of the GNU Lesser General Public
6+
# License as published by the Free Software Foundation; either
7+
# version 3.0 of the License, or (at your option) any later version.
8+
#
9+
# This library is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
# Lesser General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU Lesser General Public
15+
# License along with this library.
16+
import pytest
17+
18+
from tentacles.Meta.DSL_operators.exchange_operators.tests import (
19+
historical_prices,
20+
historical_volume,
21+
historical_times,
22+
exchange_manager_with_candles,
23+
interpreter,
24+
)
25+
26+
27+
@pytest.mark.asyncio
28+
async def test_mm_formulas_docs_examples(interpreter):
29+
# ensure examples in the docs are working (meaning returning a parsable number)
30+
assert round(await interpreter.interprete("close[-1]"), 2) == 92.22
31+
assert round(await interpreter.interprete("open[-1]"), 2) == 92.22
32+
assert round(await interpreter.interprete("high[-3]"), 2) == 92.92
33+
assert round(await interpreter.interprete("low[-1]"), 2) == 92.22
34+
assert round(await interpreter.interprete("volume[-2]"), 2) == 1211
35+
assert round(await interpreter.interprete("time[-1]"), 2) == 41
36+
assert round(await interpreter.interprete("ma(close, 12)[-1]"), 2) == 92.95
37+
assert round(await interpreter.interprete("ema(open, 24)[-1]"), 2) == 90.21
38+
assert round(await interpreter.interprete("vwma(close, volume, 4)[-1]"), 2) == 92.54
39+
assert round(await interpreter.interprete("rsi(close, 14)[-1]"), 2) == 67.55
40+
assert round(await interpreter.interprete("max(close[-1], open[-1])"), 2) == 92.22
41+
assert round(await interpreter.interprete("min(ma(close, 12)[-1], ema(open, 24)[-1])"), 2) == 90.21
42+
assert round(await interpreter.interprete("mean(close[-1], open[-1], high[-1], low[-1])"), 2) == 92.22
43+
assert round(await interpreter.interprete("round(ma(close, 12)[-1], 2)"), 2) == 92.95
44+
assert round(await interpreter.interprete("floor(close[-1])"), 2) == 92
45+
assert round(await interpreter.interprete("ceil(close[-1])"), 2) == 93
46+
assert round(await interpreter.interprete("abs(close[-1] - open[-1])"), 2) == 0
47+
assert round(await interpreter.interprete("100 if close[-1] > open[-1] else (90 + 1)"), 2) == 91

Meta/Keywords/scripting_library/configuration/tentacles_configuration.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,23 @@
2222

2323
import octobot_tentacles_manager.api
2424

25+
_EXPECTED_MAX_TENTACLES_COUNT = 256
26+
2527

2628
def get_config_history_propagated_tentacles_config_keys(tentacle: str) -> list[str]:
2729
tentacle_class = octobot_tentacles_manager.api.get_tentacle_class_from_string(tentacle)
2830
return tentacle_class.get_config_history_propagated_tentacles_config_keys()
2931

3032

3133
# cached to avoid calling default_parents_inspection when unnecessary
32-
@functools.lru_cache(maxsize=128)
34+
@functools.lru_cache(maxsize=_EXPECTED_MAX_TENTACLES_COUNT)
3335
def is_trading_mode_tentacle(tentacle_name: str) -> bool:
3436
tentacle_class = octobot_tentacles_manager.api.get_tentacle_class_from_string(tentacle_name)
3537
return tentacles_management.default_parents_inspection(tentacle_class, octobot_trading.modes.AbstractTradingMode)
3638

3739

3840
# cached to avoid calling default_parents_inspection when unnecessary
39-
@functools.lru_cache(maxsize=128)
41+
@functools.lru_cache(maxsize=_EXPECTED_MAX_TENTACLES_COUNT)
4042
def is_exchange_tentacle(tentacle_name: str) -> bool:
4143
tentacle_class = octobot_tentacles_manager.api.get_tentacle_class_from_string(tentacle_name)
4244
return tentacles_management.default_parents_inspection(tentacle_class, exchanges.RestExchange)
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
from .telegram_bot import TelegramBotInterface
1+
import octobot_commons.constants as commons_constants
2+
if not commons_constants.USE_MINIMAL_LIBS:
3+
from .telegram_bot import TelegramBotInterface

0 commit comments

Comments
 (0)