master f0ebe0c14db0 cached
1032 files
4.8 MB
1.3M tokens
3968 symbols
1 requests
Download .txt
Showing preview only (5,298K chars total). Download the full file or copy to clipboard to get everything.
Repository: Drakkar-Software/OctoBot-Tentacles
Branch: master
Commit: f0ebe0c14db0
Files: 1032
Total size: 4.8 MB

Directory structure:
gitextract_fehgh_us/

├── .coveragerc
├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── Automation/
│   ├── actions/
│   │   ├── cancel_open_order_action/
│   │   │   ├── __init__.py
│   │   │   ├── cancel_open_orders.py
│   │   │   └── metadata.json
│   │   ├── sell_all_currencies_action/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── sell_all_currencies.py
│   │   ├── send_notification_action/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── send_notification.py
│   │   └── stop_trading_action/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── stop_trading.py
│   ├── conditions/
│   │   ├── no_condition_condition/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── no_condition.py
│   │   └── scripted_condition/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── scripted_condition.py
│   └── trigger_events/
│       ├── period_check_event/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── period_check.py
│       ├── price_threshold_event/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── price_threshold.py
│       └── profitability_threshold_event/
│           ├── __init__.py
│           ├── metadata.json
│           └── profitability_threshold.py
├── Backtesting/
│   ├── collectors/
│   │   └── exchanges/
│   │       ├── exchange_bot_snapshot_data_collector/
│   │       │   ├── __init__.py
│   │       │   ├── bot_snapshot_with_history_collector.py
│   │       │   └── metadata.json
│   │       ├── exchange_history_collector/
│   │       │   ├── __init__.py
│   │       │   ├── history_collector.pxd
│   │       │   ├── history_collector.py
│   │       │   ├── metadata.json
│   │       │   └── tests/
│   │       │       ├── __init__.py
│   │       │       └── test_history_collector.py
│   │       └── exchange_live_collector/
│   │           ├── __init__.py
│   │           ├── live_collector.pxd
│   │           ├── live_collector.py
│   │           └── metadata.json
│   ├── converters/
│   │   └── exchanges/
│   │       └── legacy_data_converter/
│   │           ├── __init__.py
│   │           ├── legacy_converter.pxd
│   │           ├── legacy_converter.py
│   │           └── metadata.json
│   └── importers/
│       └── exchanges/
│           └── generic_exchange_importer/
│               ├── __init__.py
│               ├── generic_exchange_importer.pxd
│               ├── generic_exchange_importer.py
│               └── metadata.json
├── Evaluator/
│   ├── RealTime/
│   │   └── instant_fluctuations_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   ├── InstantFluctuationsEvaluator.json
│   │       │   └── InstantMAEvaluator.json
│   │       ├── instant_fluctuations.py
│   │       ├── metadata.json
│   │       └── resources/
│   │           ├── InstantFluctuationsEvaluator.md
│   │           └── InstantMAEvaluator.md
│   ├── Social/
│   │   ├── forum_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── RedditForumEvaluator.json
│   │   │   ├── forum.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── RedditForumEvaluator.md
│   │   ├── news_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── TwitterNewsEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── news.py
│   │   │   └── resources/
│   │   │       └── TwitterNewsEvaluator.md
│   │   ├── signal_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── TelegramChannelSignalEvaluator.json
│   │   │   │   └── TelegramSignalEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   ├── TelegramChannelSignalEvaluator.md
│   │   │   │   └── TelegramSignalEvaluator.md
│   │   │   ├── signal.py
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_telegram_channel_signal_evaluator.py
│   │   └── trends_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   └── GoogleTrendsEvaluator.json
│   │       ├── metadata.json
│   │       ├── resources/
│   │       │   └── GoogleTrendsEvaluator.md
│   │       └── trends.py
│   ├── Strategies/
│   │   ├── blank_strategy_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── blank_strategy.py
│   │   │   ├── config/
│   │   │   │   └── BlankStrategyEvaluator.json
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── BlankStrategyEvaluator.md
│   │   ├── dip_analyser_strategy_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── DipAnalyserStrategyEvaluator.json
│   │   │   ├── dip_analyser_strategy.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── DipAnalyserStrategyEvaluator.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_dip_analyser_strategy_evaluator.py
│   │   ├── mixed_strategies_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── SimpleStrategyEvaluator.json
│   │   │   │   └── TechnicalAnalysisStrategyEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── mixed_strategies.py
│   │   │   ├── resources/
│   │   │   │   ├── SimpleStrategyEvaluator.md
│   │   │   │   └── TechnicalAnalysisStrategyEvaluator.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       ├── test_simple_strategy_evaluator.py
│   │   │       └── test_technical_analysis_strategy_evaluator.py
│   │   └── move_signals_strategy_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   └── MoveSignalsStrategyEvaluator.json
│   │       ├── metadata.json
│   │       ├── move_signals_strategy.py
│   │       ├── resources/
│   │       │   └── MoveSignalsStrategyEvaluator.md
│   │       └── tests/
│   │           ├── __init__.py
│   │           └── test_move_signals_strategy_evaluator.py
│   ├── TA/
│   │   ├── ai_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── ai.py
│   │   │   ├── config/
│   │   │   │   └── GPTEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── GPTEvaluator.md
│   │   │   └── tests/
│   │   │       └── test_ai.py
│   │   ├── momentum_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── ADXMomentumEvaluator.json
│   │   │   │   ├── BBMomentumEvaluator.json
│   │   │   │   ├── EMAMomentumEvaluator.json
│   │   │   │   ├── KlingerOscillatorMomentumEvaluator.json
│   │   │   │   ├── KlingerOscillatorReversalConfirmationMomentumEvaluator.json
│   │   │   │   ├── MACDMomentumEvaluator.json
│   │   │   │   ├── RSIMomentumEvaluator.json
│   │   │   │   └── RSIWeightMomentumEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── momentum.py
│   │   │   ├── resources/
│   │   │   │   ├── ADXMomentumEvaluator.md
│   │   │   │   ├── BBMomentumEvaluator.md
│   │   │   │   ├── EMAMomentumEvaluator.md
│   │   │   │   ├── KlingerOscillatorMomentumEvaluator.md
│   │   │   │   ├── KlingerOscillatorReversalConfirmationMomentumEvaluator.md
│   │   │   │   ├── MACDMomentumEvaluator.md
│   │   │   │   ├── RSIMomentumEvaluator.md
│   │   │   │   └── RSIWeightMomentumEvaluator.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       ├── test_adx_momentum_evaluator.py
│   │   │       ├── test_bollinger_bands_momentum_TA_evaluator.py
│   │   │       ├── test_klinger_TA_evaluator.py
│   │   │       ├── test_macd_TA_evaluator.py
│   │   │       └── test_rsi_TA_evaluator.py
│   │   ├── trend_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── DeathAndGoldenCrossEvaluator.json
│   │   │   │   ├── DoubleMovingAverageTrendEvaluator.json
│   │   │   │   ├── EMADivergenceTrendEvaluator.json
│   │   │   │   └── SuperTrendEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   ├── DeathAndGoldenCrossEvaluator.md
│   │   │   │   ├── DoubleMovingAverageTrendEvaluator.md
│   │   │   │   ├── EMADivergenceTrendEvaluator.md
│   │   │   │   └── SuperTrendEvaluator.md
│   │   │   ├── tests/
│   │   │   │   ├── __init__.py
│   │   │   │   └── test_double_moving_averages_TA_evaluator.py
│   │   │   └── trend.py
│   │   └── volatility_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   └── StochasticRSIVolatilityEvaluator.json
│   │       ├── metadata.json
│   │       ├── resources/
│   │       │   └── StochasticRSIVolatilityEvaluator.md
│   │       └── volatility.py
│   └── Util/
│       ├── candles_util/
│       │   ├── __init__.py
│       │   ├── candles_util.pxd
│       │   ├── candles_util.py
│       │   ├── metadata.json
│       │   └── tests/
│       │       └── test_candles_util.py
│       ├── overall_state_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── overall_state_analysis.py
│       ├── pattern_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── pattern_analysis.py
│       ├── statistics_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── statistics_analysis.py
│       ├── text_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── text_analysis.py
│       └── trend_analysis/
│           ├── __init__.py
│           ├── metadata.json
│           └── trend_analysis.py
├── LICENSE
├── Meta/
│   ├── DSL_operators/
│   │   ├── exchange_operators/
│   │   │   ├── __init__.py
│   │   │   ├── exchange_operator.py
│   │   │   ├── exchange_private_data_operators/
│   │   │   │   ├── __init__.py
│   │   │   │   └── portfolio_operators.py
│   │   │   ├── exchange_public_data_operators/
│   │   │   │   ├── __init__.py
│   │   │   │   └── ohlcv_operators.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       ├── exchange_public_data_operators/
│   │   │       │   └── test_ohlcv_operators.py
│   │   │       └── test_mocks.py
│   │   ├── python_std_operators/
│   │   │   ├── __init__.py
│   │   │   ├── base_binary_operators.py
│   │   │   ├── base_call_operators.py
│   │   │   ├── base_compare_operators.py
│   │   │   ├── base_expression_operators.py
│   │   │   ├── base_iterable_operators.py
│   │   │   ├── base_name_operators.py
│   │   │   ├── base_nary_operators.py
│   │   │   ├── base_subscripting_operators.py
│   │   │   ├── base_unary_operators.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── test_base_operators.py
│   │   │       └── test_dictionnaries.py
│   │   └── ta_operators/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       ├── ta_operator.py
│   │       ├── tests/
│   │       │   ├── test_docs_examples.py
│   │       │   └── test_tulipy_technical_analysis_operators.py
│   │       └── tulipy_technical_analysis_operators.py
│   └── Keywords/
│       └── scripting_library/
│           ├── TA/
│           │   ├── __init__.py
│           │   └── trigger/
│           │       ├── __init__.py
│           │       └── eval_triggered.py
│           ├── UI/
│           │   ├── __init__.py
│           │   ├── inputs/
│           │   │   ├── __init__.py
│           │   │   ├── library_user_inputs.py
│           │   │   ├── select_candle.py
│           │   │   ├── select_history.py
│           │   │   ├── select_time_frame.py
│           │   │   └── triggers.py
│           │   └── plots/
│           │       ├── __init__.py
│           │       └── displayed_elements.py
│           ├── __init__.py
│           ├── alerts/
│           │   ├── __init__.py
│           │   └── notifications.py
│           ├── backtesting/
│           │   ├── __init__.py
│           │   ├── backtesting_data_collector.py
│           │   ├── backtesting_data_selector.py
│           │   ├── backtesting_intialization.py
│           │   ├── backtesting_settings.py
│           │   ├── default_backtesting_run_analysis_script.py
│           │   ├── metadata.py
│           │   └── run_data_analysis.py
│           ├── configuration/
│           │   ├── __init__.py
│           │   ├── exchanges_configuration.py
│           │   ├── indexes_configuration.py
│           │   ├── profile_data_configuration.py
│           │   └── tentacles_configuration.py
│           ├── constants.py
│           ├── data/
│           │   ├── __init__.py
│           │   ├── reading/
│           │   │   ├── __init__.py
│           │   │   ├── exchange_private_data/
│           │   │   │   ├── __init__.py
│           │   │   │   └── open_positions.py
│           │   │   ├── exchange_public_data.py
│           │   │   ├── metadata_reader.py
│           │   │   └── trading_settings.py
│           │   └── writing/
│           │       ├── __init__.py
│           │       ├── plotting.py
│           │       └── portfolio.py
│           ├── errors.py
│           ├── exchanges/
│           │   ├── __init__.py
│           │   └── local_exchange.py
│           ├── metadata.json
│           ├── orders/
│           │   ├── __init__.py
│           │   ├── cancelling.py
│           │   ├── chaining.py
│           │   ├── editing.py
│           │   ├── grouping.py
│           │   ├── mocks.py
│           │   ├── open_orders.py
│           │   ├── order_tags.py
│           │   ├── order_types/
│           │   │   ├── __init__.py
│           │   │   ├── create_order.py
│           │   │   ├── limit_order.py
│           │   │   ├── market_order.py
│           │   │   ├── scaled_order.py
│           │   │   ├── stop_loss_order.py
│           │   │   ├── trailing_limit_order.py
│           │   │   ├── trailing_market_order.py
│           │   │   └── trailing_stop_loss_order.py
│           │   ├── position_size/
│           │   │   ├── __init__.py
│           │   │   ├── amount.py
│           │   │   └── target_position.py
│           │   └── waiting.py
│           ├── settings/
│           │   ├── __init__.py
│           │   └── script_settings.py
│           └── tests/
│               ├── __init__.py
│               ├── backtesting/
│               │   ├── __init__.py
│               │   ├── data_store.py
│               │   ├── test_backtesting_data_collector.py
│               │   ├── test_collect_data_and_run_backtesting.py
│               │   └── test_run_data.py
│               ├── configuration/
│               │   ├── __init__.py
│               │   ├── test_indexes_configuration.py
│               │   └── test_profile_data_configuration.py
│               ├── exchanges/
│               │   └── __init__.py
│               ├── orders/
│               │   ├── __init__.py
│               │   ├── order_types/
│               │   │   ├── __init__.py
│               │   │   ├── test_create_order.py
│               │   │   ├── test_limit_order.py
│               │   │   ├── test_market_order.py
│               │   │   ├── test_multiple_orders_creation.py
│               │   │   ├── test_stop_loss_order.py
│               │   │   ├── test_trailing_limit_order.py
│               │   │   ├── test_trailing_market_order.py
│               │   │   └── test_trailing_stop_loss_order.py
│               │   ├── position_size/
│               │   │   ├── __init__.py
│               │   │   └── test_target_position.py
│               │   └── test_cancelling.py
│               ├── static/
│               │   ├── config.json
│               │   └── profile.json
│               └── test_utils/
│                   ├── __init__.py
│                   └── order_util.py
├── README.md
├── Services/
│   ├── Interfaces/
│   │   ├── telegram_bot_interface/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── telegram_bot.py
│   │   │   └── tests/
│   │   │       └── test_bot_interface.py
│   │   └── web_interface/
│   │       ├── __init__.py
│   │       ├── advanced_controllers/
│   │       │   ├── __init__.py
│   │       │   ├── configuration.py
│   │       │   ├── home.py
│   │       │   ├── matrix.py
│   │       │   ├── strategy_optimizer.py
│   │       │   └── tentacles_management.py
│   │       ├── advanced_templates/
│   │       │   ├── advanced_evaluator_config.html
│   │       │   ├── advanced_index.html
│   │       │   ├── advanced_layout.html
│   │       │   ├── advanced_matrix.html
│   │       │   ├── advanced_strategy_optimizer.html
│   │       │   ├── advanced_tentacle_packages.html
│   │       │   └── advanced_tentacles.html
│   │       ├── api/
│   │       │   ├── __init__.py
│   │       │   ├── bots.py
│   │       │   ├── config.py
│   │       │   ├── dsl.py
│   │       │   ├── exchanges.py
│   │       │   ├── feedback.py
│   │       │   ├── metadata.py
│   │       │   ├── tentacles_packages.py
│   │       │   ├── trading.py
│   │       │   ├── user_commands.py
│   │       │   └── webhook.py
│   │       ├── constants.py
│   │       ├── controllers/
│   │       │   ├── __init__.py
│   │       │   ├── about.py
│   │       │   ├── automation.py
│   │       │   ├── backtesting.py
│   │       │   ├── commands.py
│   │       │   ├── community.py
│   │       │   ├── community_authentication.py
│   │       │   ├── configuration.py
│   │       │   ├── dashboard.py
│   │       │   ├── distributions/
│   │       │   │   ├── __init__.py
│   │       │   │   └── market_making/
│   │       │   │       ├── __init__.py
│   │       │   │       ├── cloud.py
│   │       │   │       ├── configuration.py
│   │       │   │       └── dashboard.py
│   │       │   ├── dsl.py
│   │       │   ├── errors.py
│   │       │   ├── home.py
│   │       │   ├── interface_settings.py
│   │       │   ├── logs.py
│   │       │   ├── medias.py
│   │       │   ├── octobot_authentication.py
│   │       │   ├── octobot_help.py
│   │       │   ├── portfolio.py
│   │       │   ├── profiles.py
│   │       │   ├── reboot.py
│   │       │   ├── robots.py
│   │       │   ├── tentacles_config.py
│   │       │   ├── terms.py
│   │       │   ├── trading.py
│   │       │   └── welcome.py
│   │       ├── enums.py
│   │       ├── errors.py
│   │       ├── flask_util/
│   │       │   ├── __init__.py
│   │       │   ├── browsing_data_provider.py
│   │       │   ├── content_types_management.py
│   │       │   ├── context_processor.py
│   │       │   ├── cors.py
│   │       │   ├── file_services.py
│   │       │   ├── json_provider.py
│   │       │   └── template_filters.py
│   │       ├── login/
│   │       │   ├── __init__.py
│   │       │   ├── open_source_package_required.py
│   │       │   ├── user.py
│   │       │   └── web_login_manager.py
│   │       ├── metadata.json
│   │       ├── models/
│   │       │   ├── __init__.py
│   │       │   ├── backtesting.py
│   │       │   ├── commands.py
│   │       │   ├── community.py
│   │       │   ├── configuration.py
│   │       │   ├── dashboard.py
│   │       │   ├── distributions/
│   │       │   │   ├── __init__.py
│   │       │   │   └── market_making/
│   │       │   │       ├── __init__.py
│   │       │   │       └── configuration.py
│   │       │   ├── dsl.py
│   │       │   ├── interface_settings.py
│   │       │   ├── json_schemas.py
│   │       │   ├── logs.py
│   │       │   ├── medias.py
│   │       │   ├── profiles.py
│   │       │   ├── strategy_optimizer.py
│   │       │   ├── tentacles.py
│   │       │   ├── trading.py
│   │       │   └── web_interface_tab.py
│   │       ├── plugins/
│   │       │   ├── __init__.py
│   │       │   ├── abstract_plugin.py
│   │       │   └── plugin_management.py
│   │       ├── security.py
│   │       ├── static/
│   │       │   ├── css/
│   │       │   │   ├── bootstrap-editable.css
│   │       │   │   ├── components/
│   │       │   │   │   └── configuration.css
│   │       │   │   ├── layout.css
│   │       │   │   ├── style.css
│   │       │   │   └── w2ui_template.css
│   │       │   ├── distributions/
│   │       │   │   └── market_making/
│   │       │   │       └── js/
│   │       │   │           ├── configuration.js
│   │       │   │           └── dashboard.js
│   │       │   ├── js/
│   │       │   │   ├── common/
│   │       │   │   │   ├── backtesting_util.js
│   │       │   │   │   ├── bot_connection.js
│   │       │   │   │   ├── candlesticks.js
│   │       │   │   │   ├── common_handlers.js
│   │       │   │   │   ├── cst.js
│   │       │   │   │   ├── custom_elements.js
│   │       │   │   │   ├── data_collector_util.js
│   │       │   │   │   ├── dom_updater.js
│   │       │   │   │   ├── exchange_accounts.js
│   │       │   │   │   ├── feedback.js
│   │       │   │   │   ├── json_editor_settings.js
│   │       │   │   │   ├── on_load.js
│   │       │   │   │   ├── pnl_history.js
│   │       │   │   │   ├── portfolio_history.js
│   │       │   │   │   ├── required.js
│   │       │   │   │   ├── resources_rendering.js
│   │       │   │   │   ├── stepper.js
│   │       │   │   │   ├── tables_display.js
│   │       │   │   │   ├── tracking.js
│   │       │   │   │   ├── tutorial.js
│   │       │   │   │   └── util.js
│   │       │   │   └── components/
│   │       │   │       ├── advanced_matrix.js
│   │       │   │       ├── automations.js
│   │       │   │       ├── backtesting.js
│   │       │   │       ├── commands.js
│   │       │   │       ├── community.js
│   │       │   │       ├── community_metrics.js
│   │       │   │       ├── config_tentacle.js
│   │       │   │       ├── configuration.js
│   │       │   │       ├── dashboard.js
│   │       │   │       ├── dashboard_tutorial_starter.js
│   │       │   │       ├── data_collector.js
│   │       │   │       ├── dsl_help.js
│   │       │   │       ├── evaluator_configuration.js
│   │       │   │       ├── extensions.js
│   │       │   │       ├── logs.js
│   │       │   │       ├── market_status.js
│   │       │   │       ├── navbar.js
│   │       │   │       ├── portfolio.js
│   │       │   │       ├── profile_management.js
│   │       │   │       ├── profiles_selector.js
│   │       │   │       ├── strategy_optimizer.js
│   │       │   │       ├── tentacles_configuration.js
│   │       │   │       ├── trading.js
│   │       │   │       ├── trading_type_selector.js
│   │       │   │       ├── tradingview_email_config.js
│   │       │   │       └── wait_reboot.js
│   │       │   └── license.txt
│   │       ├── templates/
│   │       │   ├── 404.html
│   │       │   ├── 500.html
│   │       │   ├── about.html
│   │       │   ├── accounts.html
│   │       │   ├── automations.html
│   │       │   ├── backtesting.html
│   │       │   ├── community.html
│   │       │   ├── community_login.html
│   │       │   ├── community_metrics.html
│   │       │   ├── community_register.html
│   │       │   ├── components/
│   │       │   │   ├── community/
│   │       │   │   │   ├── bot_selector.html
│   │       │   │   │   ├── bots_stats.html
│   │       │   │   │   ├── cloud_strategies.html
│   │       │   │   │   ├── cloud_strategies_selector.html
│   │       │   │   │   ├── login.html
│   │       │   │   │   ├── octobot_cloud_description.html
│   │       │   │   │   ├── octobot_cloud_features.html
│   │       │   │   │   ├── tentacle_packages.html
│   │       │   │   │   └── user_details.html
│   │       │   │   ├── config/
│   │       │   │   │   ├── currency_card.html
│   │       │   │   │   ├── editable_config.html
│   │       │   │   │   ├── evaluator_card.html
│   │       │   │   │   ├── exchange_card.html
│   │       │   │   │   ├── notification_config.html
│   │       │   │   │   ├── profiles.html
│   │       │   │   │   ├── service_card.html
│   │       │   │   │   ├── tentacle_card.html
│   │       │   │   │   ├── tentacle_config_editor.html
│   │       │   │   │   └── trader_card.html
│   │       │   │   ├── modals/
│   │       │   │   │   ├── generic_modal.html
│   │       │   │   │   └── trading_state_modal.html
│   │       │   │   └── tentacles_packages/
│   │       │   │       └── tentacles_package_card.html
│   │       │   ├── config_tentacle.html
│   │       │   ├── data_collector.html
│   │       │   ├── distributions/
│   │       │   │   ├── default/
│   │       │   │   │   ├── footer.html
│   │       │   │   │   └── navbar.html
│   │       │   │   └── market_making/
│   │       │   │       ├── cloud.html
│   │       │   │       ├── cloud_features.html
│   │       │   │       ├── configuration.html
│   │       │   │       ├── dashboard.html
│   │       │   │       ├── footer.html
│   │       │   │       ├── interfaces.html
│   │       │   │       ├── navbar.html
│   │       │   │       └── portfolio.html
│   │       │   ├── dsl_help.html
│   │       │   ├── extensions.html
│   │       │   ├── index.html
│   │       │   ├── layout.html
│   │       │   ├── login.html
│   │       │   ├── logs.html
│   │       │   ├── macros/
│   │       │   │   ├── backtesting_utils.html
│   │       │   │   ├── cards.html
│   │       │   │   ├── critical_notifications_alert.html
│   │       │   │   ├── flash_messages.html
│   │       │   │   ├── forms.html
│   │       │   │   ├── major_issue_alert.html
│   │       │   │   ├── starting_waiter.html
│   │       │   │   ├── tables.html
│   │       │   │   ├── tentacles.html
│   │       │   │   ├── text.html
│   │       │   │   └── trading_state.html
│   │       │   ├── octobot_help.html
│   │       │   ├── portfolio.html
│   │       │   ├── profile.html
│   │       │   ├── profiles_selector.html
│   │       │   ├── robots.txt
│   │       │   ├── symbol_market_status.html
│   │       │   ├── terms.html
│   │       │   ├── trading.html
│   │       │   ├── trading_type_selector.html
│   │       │   ├── tradingview_email_config.html
│   │       │   ├── wait_reboot.html
│   │       │   └── welcome.html
│   │       ├── tests/
│   │       │   ├── __init__.py
│   │       │   ├── distribution_tester.py
│   │       │   ├── distributions/
│   │       │   │   ├── __init__.py
│   │       │   │   ├── test_default.py
│   │       │   │   └── test_market_making.py
│   │       │   └── plugin_tester.py
│   │       ├── util/
│   │       │   ├── __init__.py
│   │       │   ├── browser_util.py
│   │       │   └── flask_util.py
│   │       ├── web.py
│   │       └── websockets/
│   │           ├── __init__.py
│   │           ├── abstract_websocket_namespace_notifier.py
│   │           ├── backtesting.py
│   │           ├── dashboard.py
│   │           ├── data_collector.py
│   │           ├── notifications.py
│   │           └── strategy_optimizer.py
│   ├── Notifiers/
│   │   ├── telegram_notifier/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── telegram.py
│   │   ├── twitter_notifier/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── twitter.py
│   │   └── web_notifier/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── web.py
│   ├── Services_bases/
│   │   ├── google_service/
│   │   │   ├── __init__.py
│   │   │   ├── google.py
│   │   │   └── metadata.json
│   │   ├── gpt_service/
│   │   │   ├── __init__.py
│   │   │   ├── gpt.py
│   │   │   └── metadata.json
│   │   ├── reddit_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── reddit.py
│   │   ├── telegram_api_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── telegram_api.py
│   │   ├── telegram_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── telegram.py
│   │   ├── trading_view_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── trading_view.py
│   │   ├── twitter_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── twitter.py
│   │   ├── web_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── web.py
│   │   └── webhook_service/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── webhook.py
│   └── Services_feeds/
│       ├── google_service_feed/
│       │   ├── __init__.py
│       │   ├── google_feed.py
│       │   └── metadata.json
│       ├── reddit_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── reddit_feed.py
│       ├── telegram_api_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── telegram_api_feed.py
│       ├── telegram_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── telegram_feed.py
│       ├── trading_view_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── trading_view_feed.py
│       └── twitter_service_feed/
│           ├── __init__.py
│           ├── metadata.json
│           └── twitter_feed.py
├── Trading/
│   ├── Exchange/
│   │   ├── ascendex/
│   │   │   ├── __init__.py
│   │   │   ├── ascendex_exchange.py
│   │   │   └── metadata.json
│   │   ├── ascendex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── ascendex_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── binance/
│   │   │   ├── __init__.py
│   │   │   ├── binance_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── binance.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_sandbox.py
│   │   ├── binance_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── binance_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── binanceus/
│   │   │   ├── __init__.py
│   │   │   ├── binanceus_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── BinanceUS.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── binanceus_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── binanceus_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── bingx/
│   │   │   ├── __init__.py
│   │   │   ├── bingx_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bingx.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bingx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bingx_websocket.py
│   │   │   └── metadata.json
│   │   ├── bitfinex/
│   │   │   ├── __init__.py
│   │   │   ├── bitfinex_exchange.py
│   │   │   └── metadata.json
│   │   ├── bitfinex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bitfinex_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── bitget/
│   │   │   ├── __init__.py
│   │   │   ├── bitget_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitget.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitget_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bitget_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bithumb/
│   │   │   ├── __init__.py
│   │   │   ├── bithumb_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bithumb.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitmart/
│   │   │   ├── __init__.py
│   │   │   ├── bitmart_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── bitmart.md
│   │   ├── bitmart_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bitmart_websocket.py
│   │   │   └── metadata.json
│   │   ├── bitmex/
│   │   │   ├── __init__.py
│   │   │   ├── bitmex_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitmex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitso/
│   │   │   ├── __init__.py
│   │   │   ├── bitso_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitso.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitstamp/
│   │   │   ├── __init__.py
│   │   │   ├── bitstamp_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitstamp.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bybit/
│   │   │   ├── __init__.py
│   │   │   ├── bybit_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bybit.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bybit_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bybit_websocket.py
│   │   │   └── metadata.json
│   │   ├── coinbase/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_exchange.py
│   │   │   └── metadata.json
│   │   ├── coinbase_pro/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_pro_exchange.py
│   │   │   └── metadata.json
│   │   ├── coinbase_pro_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_pro_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── coinbase_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_websocket.py
│   │   │   └── metadata.json
│   │   ├── coinex/
│   │   │   ├── __init__.py
│   │   │   ├── coinex_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── coinex.md
│   │   ├── coinex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── coinex_websocket.py
│   │   │   └── metadata.json
│   │   ├── configurable_default_ccxt_rest/
│   │   │   ├── __init__.py
│   │   │   ├── configurable_default_rest_ccxt_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── configurable_default_rest_ccxt_exchange.md
│   │   ├── cryptocom/
│   │   │   ├── __init__.py
│   │   │   ├── cryptocom_exchange.py
│   │   │   └── metadata.json
│   │   ├── cryptocom_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── cryptocom_websocket.py
│   │   │   └── metadata.json
│   │   ├── gateio/
│   │   │   ├── __init__.py
│   │   │   ├── gateio_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── gateio.md
│   │   ├── gateio_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── gateio_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── hitbtc/
│   │   │   ├── __init__.py
│   │   │   ├── hitbtc_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hitbtc.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hollaex/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── hollaex.json
│   │   │   ├── hollaex_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hollaex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hollaex_autofilled/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── HollaexAutofilled.json
│   │   │   ├── hollaex_autofilled_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hollaex_autofilled.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hollaex_autofilled_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── hollaex_autofilled_websocket.py
│   │   │   └── metadata.json
│   │   ├── hollaex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── hollaex_websocket.py
│   │   │   └── metadata.json
│   │   ├── htx/
│   │   │   ├── __init__.py
│   │   │   ├── htx_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── htx.md
│   │   ├── htx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── htx_websocket.py
│   │   │   └── metadata.json
│   │   ├── huobi/
│   │   │   ├── __init__.py
│   │   │   ├── huobi_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── huobi.md
│   │   ├── huobi_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── huobi_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── hyperliquid/
│   │   │   ├── __init__.py
│   │   │   ├── hyperliquid_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hyperliquid.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hyperliquid_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── hyperliquid_websocket.py
│   │   │   └── metadata.json
│   │   ├── kraken/
│   │   │   ├── __init__.py
│   │   │   ├── kraken_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── kraken.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── kraken_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── kraken_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── kucoin/
│   │   │   ├── __init__.py
│   │   │   ├── kucoin_exchange.py
│   │   │   └── metadata.json
│   │   ├── kucoin_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── kucoin_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── lbank/
│   │   │   ├── __init__.py
│   │   │   ├── lbank_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── lbank.md
│   │   ├── lbank_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── lbank_websocket.py
│   │   │   └── metadata.json
│   │   ├── mexc/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── mexc_exchange.py
│   │   ├── mexc_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── mexc_websocket.py
│   │   ├── myokx/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── myokx_exchange.py
│   │   │   └── resources/
│   │   │       └── myokx.md
│   │   ├── myokx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── myokx_websocket.py
│   │   ├── ndax/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── ndax_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── ndax.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── okcoin/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okcoin_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── okcoin.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── okx/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okx_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── okx.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── okx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okx_websocket.py
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── okxus/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okxus_exchange.py
│   │   │   └── resources/
│   │   │       └── okxus.md
│   │   ├── okxus_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── okxus_websocket.py
│   │   ├── phemex/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── phemex_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── phemex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── phemex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── phemex_websocket.py
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── poloniex/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── poloniex_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── poloniex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── polymarket/
│   │   │   ├── __init__.py
│   │   │   ├── ccxt/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── polymarket_abstract.py
│   │   │   │   ├── polymarket_async.py
│   │   │   │   ├── polymarket_pro.py
│   │   │   │   └── polymarket_sync.py
│   │   │   ├── metadata.json
│   │   │   ├── polymarket_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── Polymarket.md
│   │   │   ├── script/
│   │   │   │   ├── __init__.py
│   │   │   │   └── download.py
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── polymarket_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── polymarket_websocket.py
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── upbitexchange/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── upbitexchange.md
│   │   │   ├── tests/
│   │   │   │   └── __init__.py
│   │   │   └── upbit_exchange.py
│   │   └── wavesexchange/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       ├── resources/
│   │       │   └── wavesexchange.md
│   │       ├── tests/
│   │       │   └── __init__.py
│   │       └── wavesexchange_exchange.py
│   └── Mode/
│       ├── arbitrage_trading_mode/
│       │   ├── __init__.py
│       │   ├── arbitrage_container.py
│       │   ├── arbitrage_trading.py
│       │   ├── config/
│       │   │   └── ArbitrageTradingMode.json
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── ArbitrageTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_arbitrage_container.py
│       │       ├── test_arbitrage_trading_mode_consumer.py
│       │       └── test_arbitrage_trading_mode_producer.py
│       ├── blank_trading_mode/
│       │   ├── __init__.py
│       │   ├── blank_trading.py
│       │   ├── config/
│       │   │   └── BlankTradingMode.json
│       │   ├── metadata.json
│       │   └── resources/
│       │       └── BlankTradingMode.md
│       ├── daily_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── DailyTradingMode.json
│       │   ├── daily_trading.pxd
│       │   ├── daily_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── DailyTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_daily_trading_mode.py
│       │       ├── test_daily_trading_mode_consumer.py
│       │       └── test_daily_trading_mode_producer.py
│       ├── dca_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── DCATradingMode.json
│       │   ├── dca_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── DCATradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       └── test_dca_trading_mode.py
│       ├── dip_analyser_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── DipAnalyserTradingMode.json
│       │   ├── dip_analyser_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── DipAnalyserTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       └── test_dip_analyser_trading_mode.py
│       ├── grid_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── GridTradingMode.json
│       │   ├── grid_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── GridTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── open_orders_data.py
│       │       └── test_grid_trading_mode.py
│       ├── index_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── IndexTradingMode.json
│       │   ├── index_distribution.py
│       │   ├── index_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── IndexTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_index_distribution.py
│       │       └── test_index_trading_mode.py
│       ├── market_making_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── MarketMakingTradingMode.json
│       │   ├── market_making_trading.py
│       │   ├── metadata.json
│       │   ├── order_book_distribution.py
│       │   ├── reference_price.py
│       │   ├── resources/
│       │   │   └── MarketMakingTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_market_making_trading.py
│       │       └── test_order_book_distribution.py
│       ├── remote_trading_signals_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── RemoteTradingSignalsTradingMode.json
│       │   ├── metadata.json
│       │   ├── remote_trading_signals_trading.py
│       │   ├── resources/
│       │   │   └── RemoteTradingSignalsTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_remote_trading_signals_trading_consumer.py
│       │       └── test_remote_trading_signals_trading_producer.py
│       ├── signal_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── SignalTradingMode.json
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── SignalTradingMode.md
│       │   └── signal_trading.py
│       ├── staggered_orders_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── StaggeredOrdersTradingMode.json
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── StaggeredOrdersTradingMode.md
│       │   ├── staggered_orders_trading.py
│       │   └── tests/
│       │       ├── __init__.py
│       │       └── test_staggered_orders_trading_mode.py
│       └── trading_view_signals_trading_mode/
│           ├── __init__.py
│           ├── config/
│           │   └── TradingViewSignalsTradingMode.json
│           ├── metadata.json
│           ├── resources/
│           │   └── TradingViewSignalsTradingMode.md
│           ├── tests/
│           │   ├── __init__.py
│           │   └── test_trading_view_signals_trading.py
│           └── trading_view_signals_trading.py
├── metadata.yaml
├── octobot_config.json
├── profiles/
│   ├── arbitrage_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── ArbitrageTradingMode.json
│   │   └── tentacles_config.json
│   ├── copy_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── RemoteTradingSignalsTradingMode.json
│   │   └── tentacles_config.json
│   ├── daily_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DailyTradingMode.json
│   │   │   └── SimpleStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── dip_analyser/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DipAnalyserStrategyEvaluator.json
│   │   │   ├── DipAnalyserTradingMode.json
│   │   │   ├── InstantFluctuationsEvaluator.json
│   │   │   └── RSIWeightMomentumEvaluator.json
│   │   └── tentacles_config.json
│   ├── gpt_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DCATradingMode.json
│   │   │   ├── GPTEvaluator.json
│   │   │   └── SimpleStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── grid_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── GridTradingMode.json
│   │   └── tentacles_config.json
│   ├── index_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── IndexTradingMode.json
│   │   └── tentacles_config.json
│   ├── market_making/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── MarketMakingTradingMode.json
│   │   └── tentacles_config.json
│   ├── non-trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── BlankStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── signal_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── MoveSignalsStrategyEvaluator.json
│   │   │   └── SignalTradingMode.json
│   │   └── tentacles_config.json
│   ├── simple_dca/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── DCATradingMode.json
│   │   └── tentacles_config.json
│   ├── smart_dca/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DCATradingMode.json
│   │   │   ├── EMAMomentumEvaluator.json
│   │   │   └── SimpleStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── staggered_orders_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── StaggeredOrdersTradingMode.json
│   │   └── tentacles_config.json
│   ├── tradingview_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── TradingViewSignalsTradingMode.json
│   │   └── tentacles_config.json
│   └── trailing_grid_trading/
│       ├── profile.json
│       ├── specific_config/
│       │   └── GridTradingMode.json
│       └── tentacles_config.json
└── scripts/
    └── clear_cloudflare_cache.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
[run]
include =
    tentacles/


================================================
FILE: .github/workflows/main.yml
================================================
name: OctoBot-Tentacles-CI
on:
  push:
    branches:
      - 'master'
      - 'dev'
      - 'beta'
    tags:
      - '*'
  pull_request:

jobs:
  tests:
    name: ${{ matrix.os }}${{ matrix.arch }} - Python - ${{ matrix.python }} - Tests
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [windows-latest, ubuntu-latest ]
        arch: [ x64 ]
        python: [ '3.10' ]

    steps:
    - uses: actions/checkout@v5
    - name: Set up Python ${{ matrix.python }}
      uses: actions/setup-python@v6
      with:
        python-version: ${{ matrix.python }}
        architecture: ${{ matrix.arch }}

    - name: Install OctoBot on Unix
      if: matrix.os != 'windows-latest'
      env:
        OCTOBOT_GH_REPO: https://github.com/Drakkar-Software/OctoBot.git
        OCTOBOT_DEFAULT_BRANCH: dev
      run: |
        echo "GITHUB_REF=$GITHUB_REF"
        TARGET_BRANCH=$([ "$GITHUB_HEAD_REF" == "" ] && echo ${GITHUB_REF##*/} || echo "$GITHUB_HEAD_REF")
        git clone -q $OCTOBOT_GH_REPO -b ${TARGET_BRANCH} || git clone -q $OCTOBOT_GH_REPO -b $OCTOBOT_DEFAULT_BRANCH
        cd OctoBot
        git status
        pip install --prefer-binary -r dev_requirements.txt -r requirements.txt -r full_requirements.txt
        cd ..
        mkdir new_tentacles
        cp -r Automation Backtesting Evaluator Meta Services Trading profiles new_tentacles
        cd OctoBot
        python start.py tentacles -d "../new_tentacles" -p "../../any_platform.zip"
        python start.py tentacles --install --location "../any_platform.zip" --all

    - name: Install OctoBot on Windows
      if: matrix.os == 'windows-latest'
      env:
        OCTOBOT_GH_REPO: https://github.com/Drakkar-Software/OctoBot.git
        OCTOBOT_DEFAULT_BRANCH: dev
      run: |
        echo "GITHUB_REF=$env:GITHUB_REF"
        $env:TARGET_BRANCH = $env:GITHUB_REF
        If ((Test-Path env:GITHUB_HEAD_REF) -and -not ([string]::IsNullOrWhiteSpace($env:GITHUB_HEAD_REF))) {
          echo "using GITHUB_HEAD_REF"
          $env:TARGET_BRANCH = $env:GITHUB_HEAD_REF
        }
        echo "TARGET_BRANCH=$env:TARGET_BRANCH"
        If ($env:TARGET_BRANCH -notcontains "refs/tags/") {
          $env:TENTACLES_URL_TAG = "latest"
        }
        echo "cleaned TARGET_BRANCH=$env:TARGET_BRANCH"
        git clone -q $env:OCTOBOT_GH_REPO -b $env:TARGET_BRANCH.Replace('refs/heads/','')
        if ($LastExitCode -ne 0) {
          git clone -q $env:OCTOBOT_GH_REPO -b $env:OCTOBOT_DEFAULT_BRANCH
        }
        cd OctoBot
        git status
        pip install --upgrade pip setuptools wheel
        pip install --prefer-binary -r dev_requirements.txt -r requirements.txt -r full_requirements.txt
        cd ..
        mkdir new_tentacles
        xcopy Automation new_tentacles\\Automation /E/H/I
        xcopy Backtesting new_tentacles\\Backtesting /E/H/I
        xcopy Evaluator new_tentacles\\Evaluator /E/H/I
        xcopy Meta new_tentacles\\Meta /E/H/I
        xcopy Services new_tentacles\\Services /E/H/I
        xcopy Trading new_tentacles\\Trading /E/H/I
        xcopy profiles new_tentacles\\profiles /E/H/I
        cd OctoBot
        python start.py tentacles -d "../new_tentacles" -p "../../any_platform.zip"
        python start.py tentacles --install --location "../any_platform.zip" --all
      shell: powershell

    - name: Pytests
      run: |
        cd OctoBot
        pytest --cov=. --cov-config=.coveragerc --durations=0 -rw --ignore=tentacles/Trading/Exchange tentacles

    - name: Publish coverage
      if: github.event_name == 'push'
      continue-on-error: true
      run: coveralls
      env:
        COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

  upload_tentacles:
    needs: tests
    name: ${{ matrix.os }}${{ matrix.arch }} - Python - ${{ matrix.python }} - Upload
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ ubuntu-latest ]
        arch: [ x64 ]
        python: [ '3.10' ]

    steps:
      - uses: actions/checkout@v5
      - name: Set Environment Variables
        run: |
          echo "S3_API_KEY=${{ secrets.S3_API_KEY }}" >> $GITHUB_ENV
          echo "S3_API_SECRET_KEY=${{ secrets.S3_API_SECRET_KEY }}" >> $GITHUB_ENV
          echo "S3_REGION_NAME=${{ secrets.S3_REGION_NAME }}" >> $GITHUB_ENV
          echo "S3_ENDPOINT_URL=${{ secrets.S3_ENDPOINT_URL }}" >> $GITHUB_ENV
          echo "CLOUDFLARE_TOKEN=${{ secrets.CLOUDFLARE_TOKEN }}" >> $GITHUB_ENV
          echo "CLOUDFLARE_ZONE=${{ secrets.CLOUDFLARE_ZONE }}" >> $GITHUB_ENV
          TARGET_BRANCH=$([ "$GITHUB_HEAD_REF" == "" ] && echo ${GITHUB_REF##*/} || echo "$GITHUB_HEAD_REF")
          echo "TARGET_BRANCH=${TARGET_BRANCH}" >> $GITHUB_ENV

      - name: Set up Python ${{ matrix.python }}
        uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python }}
          architecture: ${{ matrix.arch }}

      - name: Produce tentacles package
        env:
          OCTOBOT_GH_REPO: https://github.com/Drakkar-Software/OctoBot.git
          OCTOBOT_DEFAULT_BRANCH: dev
        run: |
          git clone -q $OCTOBOT_GH_REPO -b ${TARGET_BRANCH} || git clone -q $OCTOBOT_GH_REPO -b $OCTOBOT_DEFAULT_BRANCH
          cd OctoBot
          git status
          pip install --prefer-binary -r dev_requirements.txt -r requirements.txt -r full_requirements.txt
          cd ..
          mkdir new_tentacles
          cp -r Automation Backtesting Evaluator Meta Services Trading profiles new_tentacles

      - name: Publish tag tentacles
        if: startsWith(github.ref, 'refs/tags')
        env:
          S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
        run: |
          sed -i "s/VERSION_PLACEHOLDER/${TARGET_BRANCH#refs/*/}/g" metadata.yaml
          cd OctoBot
          python start.py tentacles -m "../metadata.yaml" -d "../new_tentacles" -p "../../any_platform.zip" -ite -ute ${{ secrets.TENTACLES_OFFICIAL_PATH }}/tentacles -upe ${{ secrets.TENTACLES_OFFICIAL_PATH }}/packages/full/${{ secrets.TENTACLES_REPOSITORY_NAME }}/
          python ../scripts/clear_cloudflare_cache.py ${TARGET_BRANCH#refs/*/}

      - name: Publish latest tentacles
        if: github.ref == 'refs/heads/dev' && startsWith(github.ref, 'refs/tags') != true
        env:
          S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
        run: |
          sed -i "s/VERSION_PLACEHOLDER/latest/g" metadata.yaml
          cd OctoBot
          python start.py tentacles -m "../metadata.yaml" -d "../new_tentacles" -p "../../any_platform.zip" -upe ${{ secrets.TENTACLES_OFFICIAL_PATH }}/packages/full/${{ secrets.TENTACLES_REPOSITORY_NAME }}/
          python ../scripts/clear_cloudflare_cache.py latest

      - name: Publish stable tentacles
        if: github.ref == 'refs/heads/master'
        env:
          S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
        run: |
          sed -i "s/VERSION_PLACEHOLDER/stable/g" metadata.yaml
          cd OctoBot
          python start.py tentacles -m "../metadata.yaml" -d "../new_tentacles" -p "../../any_platform.zip" -upe ${{ secrets.TENTACLES_OFFICIAL_PATH }}/packages/full/${{ secrets.TENTACLES_REPOSITORY_NAME }}/
          python ../scripts/clear_cloudflare_cache.py stable

      - name: Publish cleaned branch tentacles
        if: startsWith(github.ref, 'refs/tags') != true && github.ref != 'refs/heads/master'
        env:
          S3_BUCKET_NAME: ${{ secrets.S3_DEV_BUCKET_NAME }}
        run: |
          branch="${TARGET_BRANCH##*/}"
          sed -i "s/VERSION_PLACEHOLDER/$branch/g" metadata.yaml
          sed -i "s/base/$branch/g" metadata.yaml
          sed -i "s/officials/dev/g" metadata.yaml
          cd OctoBot
          python start.py tentacles -m "../metadata.yaml" -d "../new_tentacles" -p "../../any_platform.zip" -upe ${{ secrets.TENTACLES_OFFICIAL_PATH }}/packages/full/${{ secrets.TENTACLES_REPOSITORY_NAME }}/
          python ../scripts/clear_cloudflare_cache.py $branch

  notify:
    if: ${{ failure() }}
    needs:
      - tests
      - upload_tentacles
    uses: Drakkar-Software/.github/.github/workflows/failure_notify_workflow.yml@master
    secrets:
      DISCORD_GITHUB_WEBHOOK: ${{ secrets.DISCORD_GITHUB_WEBHOOK }}


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

\.idea/


/__init__.py
Backtesting/__init__.py
Backtesting/collectors/__init__.py
Backtesting/collectors/exchanges/__init__.py
Backtesting/converters/__init__.py
Backtesting/converters/exchanges/__init__.py
Backtesting/importers/__init__.py
Backtesting/importers/exchanges/__init__.py
Evaluator/__init__.py
Evaluator/RealTime/__init__.py
Evaluator/Social/__init__.py
Evaluator/Strategies/__init__.py
Evaluator/TA/__init__.py
Evaluator/Util/__init__.py
profiles/__init__.py
Services/__init__.py
Services/Interfaces/__init__.py
Services/Notifiers/__init__.py
Services/Services_bases/__init__.py
Services/Services_feeds/__init__.py
Trading/__init__.py
Trading/Exchange/__init__.py
Trading/Mode/__init__.py

================================================
FILE: Automation/actions/cancel_open_order_action/__init__.py
================================================
from .cancel_open_orders import CancelOpenOrders

================================================
FILE: Automation/actions/cancel_open_order_action/cancel_open_orders.py
================================================
#  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 asyncio

import octobot_commons.configuration as configuration
import octobot_trading.api as trading_api
import octobot.automation.bases.abstract_action as abstract_action


class CancelOpenOrders(abstract_action.AbstractAction):
    async def process(self):
        exchange_managers = trading_api.get_exchange_managers_from_exchange_ids(trading_api.get_exchange_ids())
        await asyncio.gather(*(
            trading_api.cancel_all_open_orders(exchange_manager)
            for exchange_manager in exchange_managers
        ))

    @staticmethod
    def get_description() -> str:
        return "Cancel all OctoBot-managed open orders on each exchange."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {}

    def apply_config(self, config):
        # no config
        pass


================================================
FILE: Automation/actions/cancel_open_order_action/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["CancelOpenOrders"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/actions/sell_all_currencies_action/__init__.py
================================================
from .sell_all_currencies import SellAllCurrencies

================================================
FILE: Automation/actions/sell_all_currencies_action/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["SellAllCurrencies"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/actions/sell_all_currencies_action/sell_all_currencies.py
================================================
#  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 asyncio

import octobot_commons.configuration as configuration
import octobot_trading.api as trading_api
import octobot.automation.bases.abstract_action as abstract_action


class SellAllCurrencies(abstract_action.AbstractAction):
    async def process(self):
        exchange_managers = trading_api.get_exchange_managers_from_exchange_ids(trading_api.get_exchange_ids())
        await asyncio.gather(*(
            trading_api.sell_all_everything_for_reference_market(exchange_manager)
            for exchange_manager in exchange_managers
        ))

    @staticmethod
    def get_description() -> str:
        return "Market sell each currency for the reference market on each exchange."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {}

    def apply_config(self, config):
        # no config
        pass


================================================
FILE: Automation/actions/send_notification_action/__init__.py
================================================
from .send_notification import SendNotification

================================================
FILE: Automation/actions/send_notification_action/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["SendNotification"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/actions/send_notification_action/send_notification.py
================================================
#  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 octobot_commons.enums as commons_enums
import octobot_commons.configuration as configuration
import octobot_services.enums as services_enums
import octobot_services.api as services_api
import octobot.automation.bases.abstract_action as abstract_action


class SendNotification(abstract_action.AbstractAction):
    MESSAGE = "message"

    def __init__(self):
        super().__init__()
        self.notification_message = None

    async def process(self):
        await services_api.send_notification(
            services_api.create_notification(
                self.notification_message,
                category=services_enums.NotificationCategory.OTHER
            )
        )

    @staticmethod
    def get_description() -> str:
        return f"Sends the configured message. " \
               f"Configure notification channels in the 'Accounts' tab. " \
               f"The notification type is '{services_enums.NotificationCategory.OTHER.value.capitalize()}'."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {
            self.MESSAGE: UI.user_input(
                self.MESSAGE, commons_enums.UserInputTypes.TEXT, "Your notification triggered", inputs,
                title="Message to include in your notification.",
                parent_input_name=step_name,
            )
        }

    def apply_config(self, config):
        self.notification_message = config[self.MESSAGE]


================================================
FILE: Automation/actions/stop_trading_action/__init__.py
================================================
from .stop_trading import StopTrading

================================================
FILE: Automation/actions/stop_trading_action/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["StopTrading"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/actions/stop_trading_action/stop_trading.py
================================================
#  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 octobot_commons.constants as commons_constants
import octobot_services.interfaces.util as interfaces_util
import tentacles.Automation.actions.cancel_open_order_action as cancel_open_orders


class StopTrading(cancel_open_orders.CancelOpenOrders):
    PROFILE_ID = commons_constants.DEFAULT_PROFILE  # non trading profile

    async def process(self):
        # cancel all open orders
        await super().process()
        # select non trading profile
        config = interfaces_util.get_edited_config(dict_only=False)
        config.select_profile(self.PROFILE_ID)
        config.save()
        # reboot
        interfaces_util.get_bot_api().restart_bot()

    @staticmethod
    def get_description() -> str:
        return "Cancel all OctoBot-managed open orders on each exchange, switch to the Non-Trading profile " \
               "and restart OctoBot."


================================================
FILE: Automation/conditions/no_condition_condition/__init__.py
================================================
from .no_condition import NoCondition

================================================
FILE: Automation/conditions/no_condition_condition/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["NoCondition"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/conditions/no_condition_condition/no_condition.py
================================================
#  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 octobot_commons.configuration as configuration
import octobot.automation.bases.abstract_condition as abstract_condition


class NoCondition(abstract_condition.AbstractCondition):
    async def evaluate(self) -> bool:
        return True

    @staticmethod
    def get_description() -> str:
        return "Is always passing."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {}

    def apply_config(self, config):
        # no config
        pass


================================================
FILE: Automation/conditions/scripted_condition/__init__.py
================================================
from .scripted_condition import ScriptedCondition

================================================
FILE: Automation/conditions/scripted_condition/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["ScriptedCondition"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/conditions/scripted_condition/scripted_condition.py
================================================
#  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 typing

import octobot_commons.configuration as configuration
import octobot_trading.api as trading_api
import octobot_commons.enums as commons_enums
import octobot.automation.bases.abstract_condition as abstract_condition
import octobot_commons.dsl_interpreter as dsl_interpreter

import tentacles.Meta.DSL_operators as dsl_operators


class ScriptedCondition(abstract_condition.AbstractCondition):
    SCRIPT = "script"
    EXCHANGE = "exchange"

    def __init__(self):
        super().__init__()
        self.script: str = ""
        self.exchange_name: str = ""

        self._dsl_interpreter: typing.Optional[dsl_interpreter.Interpreter] = None

    async def evaluate(self) -> bool:
        if self._dsl_interpreter:
            script_result = await self._dsl_interpreter.interprete(self.script)
            return bool(script_result)
        raise ValueError("Scripted condition is not properly configured, the script is likely invalid.")

    @staticmethod
    def get_description() -> str:
        return "Evaluates a scripted condition using the OctoBot DSL."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        exchanges = list(trading_api.get_exchange_names())
        return {
            self.SCRIPT: UI.user_input(
                self.SCRIPT, commons_enums.UserInputTypes.TEXT, "", inputs,
                title="Scripted condition: the OctoBot DSL expression to evaluate (more info in automation details). Its return value will be converted to a boolean using \"bool()\" to determine if the condition is met.",
                parent_input_name=step_name,
            ),
            self.EXCHANGE: UI.user_input(
                self.EXCHANGE, commons_enums.UserInputTypes.OPTIONS, exchanges[0], inputs,
                options=exchanges,
                title="Exchange: the name of the exchange to use for the condition.",
                parent_input_name=step_name,
            )
        }

    def apply_config(self, config):
        self.script = config[self.SCRIPT]
        self.exchange_name = config[self.EXCHANGE]
        if self.script and self.exchange_name:
            self._dsl_interpreter = self._create_dsl_interpreter()
            self._validate_script()
        else:
            self._dsl_interpreter = None
    
    def _validate_script(self):
        try:
            self._dsl_interpreter.prepare(self.script)
            self.logger.info(
                f"Formula interpreter successfully prepared \"{self.script}\" condition"
            )
        except Exception as e:
            self.logger.error(f"Error when parsing condition {self.script}: {e}")
            raise e

    def _create_dsl_interpreter(self):
        exchange_manager = self._get_exchange_manager()
        ohlcv_operators = []
        portfolio_operators = []
        if exchange_manager is not None:
            ohlcv_operators = dsl_operators.exchange_operators.create_ohlcv_operators(
                exchange_manager, None, None
            )
            portfolio_operators = dsl_operators.exchange_operators.create_portfolio_operators(
                exchange_manager
            )
        return dsl_interpreter.Interpreter(
            dsl_interpreter.get_all_operators() + ohlcv_operators + portfolio_operators
        )
    
    def _get_exchange_manager(self):
        for exchange_id in trading_api.get_exchange_ids():
            exchange_manager = trading_api.get_exchange_manager_from_exchange_id(exchange_id)
            if exchange_manager.exchange_name == self.exchange_name and exchange_manager.is_backtesting == False:
                return exchange_manager
        raise ValueError(f"No exchange manager found for exchange name: {self.exchange_name}")


================================================
FILE: Automation/trigger_events/period_check_event/__init__.py
================================================
from .period_check import PeriodicCheck

================================================
FILE: Automation/trigger_events/period_check_event/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["PeriodicCheck"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/trigger_events/period_check_event/period_check.py
================================================
#  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 asyncio

import octobot_commons.enums as commons_enums
import octobot_commons.configuration as configuration
import octobot.automation.bases.abstract_trigger_event as abstract_trigger_event


class PeriodicCheck(abstract_trigger_event.AbstractTriggerEvent):
    UPDATE_PERIOD = "update_period"

    def __init__(self):
        super().__init__()
        self.waiter_task = None
        self.waiting_time = None

    async def stop(self):
        await super().stop()
        if self.waiter_task is not None and not self.waiter_task.done():
            self.waiter_task.cancel()

    async def _get_next_event(self):
        if self.should_stop:
            raise StopIteration
        self.waiter_task = asyncio.create_task(asyncio.sleep(self.waiting_time))
        await self.waiter_task

    @staticmethod
    def get_description() -> str:
        return "Will trigger periodically, at the specified update period."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {
            self.UPDATE_PERIOD: UI.user_input(
                self.UPDATE_PERIOD, commons_enums.UserInputTypes.FLOAT, 300, inputs,
                title="Update period: number of seconds to wait between each update.",
                parent_input_name=step_name,
            )
        }

    def apply_config(self, config):
        self.waiting_time = config[self.UPDATE_PERIOD]


================================================
FILE: Automation/trigger_events/price_threshold_event/__init__.py
================================================
from .price_threshold import PriceThreshold

================================================
FILE: Automation/trigger_events/price_threshold_event/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["PriceThreshold"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/trigger_events/price_threshold_event/price_threshold.py
================================================
#  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 asyncio
import decimal

import async_channel.enums as channel_enums
import octobot_commons.enums as commons_enums
import octobot_commons.configuration as configuration
import octobot_commons.channels_name as channels_name
import octobot.automation.bases.abstract_trigger_event as abstract_trigger_event
import octobot_trading.exchange_channel as exchanges_channel
import octobot_trading.api as trading_api


class PriceThreshold(abstract_trigger_event.AbstractTriggerEvent):
    TARGET_PRICE = "target_price"
    SYMBOL = "symbol"
    TRIGGER_ONLY_ONCE = "trigger_only_once"
    MAX_TRIGGER_FREQUENCY = "max_trigger_frequency"

    def __init__(self):
        super().__init__()
        self.waiter_task = None
        self.symbol = None
        self.target_price = None
        self.last_price = None
        self.trigger_event = asyncio.Event()
        self.registered_consumer = False
        self.consumers = []

    async def _register_consumer(self):
        self.registered_consumer = True
        for exchange_id in trading_api.get_exchange_ids():
            self.consumers.append(
                await exchanges_channel.get_chan(
                    channels_name.OctoBotTradingChannelsName.MARK_PRICE_CHANNEL.value,
                    exchange_id
                ).new_consumer(
                    self.mark_price_callback,
                    priority_level=channel_enums.ChannelConsumerPriorityLevels.MEDIUM.value,
                    symbol=self.symbol
                )
            )

    async def mark_price_callback(
            self, exchange: str, exchange_id: str, cryptocurrency: str, symbol: str, mark_price
    ):
        if self.should_stop:
            # do not go any further if the action has been stopped
            return
        self._check_threshold(mark_price)
        self._update_last_price(mark_price)

    def _update_last_price(self, mark_price):
        self.last_price = mark_price

    def _check_threshold(self, mark_price):
        if self.last_price is None:
            return
        if mark_price >= self.target_price > self.last_price or mark_price <= self.target_price < self.last_price:
            # mark_price crossed self.target_price threshold
            self.trigger_event.set()

    async def stop(self):
        await super().stop()
        if self.waiter_task is not None and not self.waiter_task.done():
            self.waiter_task.cancel()
        for consumer in self.consumers:
            await consumer.stop()
        self.consumers = []

    async def _get_next_event(self):
        if self.should_stop:
            raise StopIteration
        if not self.registered_consumer:
            await self._register_consumer()
        self.waiter_task = asyncio.create_task(asyncio.wait_for(self.trigger_event.wait(), timeout=None))
        await self.waiter_task
        self.trigger_event.clear()

    @staticmethod
    def get_description() -> str:
        return "Will trigger when the price of the given symbol crosses the given price."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {
            self.SYMBOL: UI.user_input(
                self.SYMBOL, commons_enums.UserInputTypes.TEXT, "BTC/USDT", inputs,
                title="Symbol: symbol to watch price on. Example: ETH/BTC or BTC/USDT:USDT",
                parent_input_name=step_name,
            ),
            self.TARGET_PRICE: UI.user_input(
                self.TARGET_PRICE, commons_enums.UserInputTypes.FLOAT, 300, inputs,
                title="Target price: price triggering the event.",
                parent_input_name=step_name,
            ),
            self.MAX_TRIGGER_FREQUENCY: UI.user_input(
                self.MAX_TRIGGER_FREQUENCY, commons_enums.UserInputTypes.FLOAT, 0.0, inputs,
                title="Maximum trigger frequency: required time between each trigger. In seconds. "
                      "Useful to avoid spamming in certain situations.",
                parent_input_name=step_name,
            ),
            self.TRIGGER_ONLY_ONCE: UI.user_input(
                self.TRIGGER_ONLY_ONCE, commons_enums.UserInputTypes.BOOLEAN, False, inputs,
                title="Trigger only once: can only trigger once until OctoBot restart or "
                      "the automation configuration changes.",
                parent_input_name=step_name,
            ),
        }

    def apply_config(self, config):
        self.trigger_event.clear()
        self.last_price = None
        self.symbol = config[self.SYMBOL]
        self.target_price = decimal.Decimal(str(config[self.TARGET_PRICE]))
        self.trigger_only_once = config[self.TRIGGER_ONLY_ONCE]
        self.max_trigger_frequency = config[self.MAX_TRIGGER_FREQUENCY]


================================================
FILE: Automation/trigger_events/profitability_threshold_event/__init__.py
================================================
from .profitability_threshold import ProfitabilityThreshold

================================================
FILE: Automation/trigger_events/profitability_threshold_event/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["ProfitabilityThreshold"],
  "tentacles-requirements": []
}

================================================
FILE: Automation/trigger_events/profitability_threshold_event/profitability_threshold.py
================================================
#  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 asyncio
import decimal
import time
import sortedcontainers

import async_channel.enums as channel_enums
import octobot_commons.enums as commons_enums
import octobot_commons.constants as commons_constants
import octobot_commons.configuration as configuration
import octobot_commons.channels_name as channels_name
import octobot.automation.bases.abstract_trigger_event as abstract_trigger_event
import octobot_trading.exchange_channel as exchanges_channel
import octobot_trading.api as trading_api
import octobot_trading.constants as trading_constants


class ProfitabilityThreshold(abstract_trigger_event.AbstractTriggerEvent):
    PERCENT_CHANGE = "percent_change"
    TIME_PERIOD = "time_period"
    TRIGGER_ONLY_ONCE = "trigger_only_once"
    MAX_TRIGGER_FREQUENCY = "max_trigger_frequency"

    def __init__(self):
        super().__init__()
        self.waiter_task = None
        self.percent_change = None
        self.time_period = None
        self.profitability_by_time = None
        self.trigger_event = asyncio.Event()
        self.registered_consumer = False
        self.consumers = []

    async def _register_consumer(self):
        self.registered_consumer = True
        for exchange_id in trading_api.get_exchange_ids():
            self.consumers.append(
                await exchanges_channel.get_chan(
                    channels_name.OctoBotTradingChannelsName.BALANCE_PROFITABILITY_CHANNEL.value,
                    exchange_id
                ).new_consumer(
                    self.balance_profitability_callback,
                    priority_level=channel_enums.ChannelConsumerPriorityLevels.MEDIUM.value
                )
            )

    async def balance_profitability_callback(
            self,
            exchange: str,
            exchange_id: str,
            profitability,
            profitability_percent,
            market_profitability_percent,
            initial_portfolio_current_profitability,
    ):
        if self.should_stop:
            # do not go any further if the action has been stopped
            return
        self._update_profitability_by_time(profitability_percent)
        self._check_threshold(profitability_percent)

    def _update_profitability_by_time(self, profitability_percent):
        self.profitability_by_time[int(time.time())] = profitability_percent
        current_time = time.time()
        for profitability_time in list(self.profitability_by_time):
            if profitability_time - current_time > self.time_period:
                self.profitability_by_time.pop(profitability_time)

    def _check_threshold(self, profitability_percent):
        oldest_compared_profitability = next(iter(self.profitability_by_time.values()))
        if trading_constants.ZERO < self.percent_change <= profitability_percent - oldest_compared_profitability:
            # profitability_percent reached or when above self.percent_change
            self.trigger_event.set()
        if trading_constants.ZERO > self.percent_change >= profitability_percent - oldest_compared_profitability:
            # profitability_percent reached or when bellow self.percent_change
            self.trigger_event.set()

    async def stop(self):
        await super().stop()
        if self.waiter_task is not None and not self.waiter_task.done():
            self.waiter_task.cancel()
        for consumer in self.consumers:
            await consumer.stop()
        self.consumers = []

    async def _get_next_event(self):
        if self.should_stop:
            raise StopIteration
        if not self.registered_consumer:
            await self._register_consumer()
        self.waiter_task = asyncio.create_task(asyncio.wait_for(self.trigger_event.wait(), timeout=None))
        await self.waiter_task
        self.trigger_event.clear()

    @staticmethod
    def get_description() -> str:
        return "Will trigger when profitability reaches the given % change on the given time window. " \
               "Example: a Percent change of 10 will trigger the automation if your OctoBot profitability " \
               "changes from 0 to 10 or from 30 to 40."

    def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
        return {
            self.PERCENT_CHANGE: UI.user_input(
                self.PERCENT_CHANGE, commons_enums.UserInputTypes.FLOAT, 35, inputs,
                title="Percent change: minimum change of % profitability to trigger the automation. "
                      "Can be negative to trigger on losses.",
                parent_input_name=step_name,
            ),
            self.TIME_PERIOD: UI.user_input(
                self.TIME_PERIOD, commons_enums.UserInputTypes.FLOAT, 300, inputs,
                title="Time period: maximum time to consider to compute profitability changes. In minutes.",
                parent_input_name=step_name,
            ),
            self.MAX_TRIGGER_FREQUENCY: UI.user_input(
                self.MAX_TRIGGER_FREQUENCY, commons_enums.UserInputTypes.FLOAT, 0.0, inputs,
                title="Maximum trigger frequency: required time between each trigger. In seconds. "
                      "Useful to avoid spamming in certain situations.",
                parent_input_name=step_name,
            ),
            self.TRIGGER_ONLY_ONCE: UI.user_input(
                self.TRIGGER_ONLY_ONCE, commons_enums.UserInputTypes.BOOLEAN, False, inputs,
                title="Trigger only once: can only trigger once until OctoBot restart or "
                      "the automation configuration changes.",
                parent_input_name=step_name,
            ),
        }

    def apply_config(self, config):
        self.trigger_event.clear()
        self.profitability_by_time = sortedcontainers.SortedDict()
        self.percent_change = decimal.Decimal(str(config[self.PERCENT_CHANGE]))
        self.time_period = config[self.TIME_PERIOD] * commons_constants.MINUTE_TO_SECONDS
        self.trigger_only_once = config[self.TRIGGER_ONLY_ONCE]
        self.max_trigger_frequency = config[self.MAX_TRIGGER_FREQUENCY]


================================================
FILE: Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/__init__.py
================================================
from .bot_snapshot_with_history_collector import ExchangeBotSnapshotWithHistoryCollector

================================================
FILE: Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/bot_snapshot_with_history_collector.py
================================================
#  Drakkar-Software OctoBot
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import asyncio
import copy
import os
import json
import time
import shutil
import collections

import octobot_backtesting.collectors as collector
import octobot_backtesting.importers as importers
import octobot_backtesting.enums as backtesting_enums
import octobot_backtesting.constants as backtesting_constants
import octobot_backtesting.errors as backtesting_errors
import octobot_commons.errors as commons_errors
import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.symbols.symbol_util as symbol_util
import octobot_commons.databases as databases
import octobot_backtesting.data as data
import octobot_trading.api as trading_api
import octobot_trading.errors as trading_errors
import tentacles.Backtesting.importers.exchanges.generic_exchange_importer as generic_exchange_importer



class ExchangeBotSnapshotWithHistoryCollector(collector.AbstractExchangeBotSnapshotCollector):
    IMPORTER = generic_exchange_importer.GenericExchangeDataImporter
    OHLCV = "ohlcv"
    KLINE = "kline"

    def __init__(self, config, exchange_name, exchange_type, tentacles_setup_config, symbols, time_frames,
                 use_all_available_timeframes=False,
                 data_format=backtesting_enums.DataFormats.REGULAR_COLLECTOR_DATA,
                 start_timestamp=None,
                 end_timestamp=None):
        super().__init__(config, exchange_name, exchange_type, tentacles_setup_config, symbols, time_frames,
                         use_all_available_timeframes, data_format=data_format,
                         start_timestamp=start_timestamp, end_timestamp=end_timestamp)
        self.exchange_type = None
        self.exchange_manager = None
        self.fetch_exchange_manager = None
        self.file_name = data.get_backtesting_file_name(self.__class__,
                                                        self.get_permanent_file_identifier,
                                                        data_format=data_format)
        self.is_creating_database = False
        self.description = None
        self.missing_symbols = []
        self.fetched_data = {
            self.OHLCV: {},
            self.KLINE: {},
        }
        self.set_file_path()

    def get_permanent_file_identifier(self):
        symbols = "-".join(symbol_util.merge_symbol(symbol.symbol_str) for symbol in self.symbols)
        time_frames = "-".join(tf.value for tf in self.time_frames)
        return f"{self.exchange_name}{backtesting_constants.BACKTESTING_DATA_FILE_SEPARATOR}" \
               f"{symbols}{backtesting_constants.BACKTESTING_DATA_FILE_SEPARATOR}{time_frames}"

    async def initialize(self):
        self.create_database()
        await self.database.initialize()
        await self._check_database_content()

    def set_file_path(self) -> None:
        super().set_file_path()
        if os.path.isfile(self.file_path):
            shutil.copy(self.file_path, self.temp_file_path)

    def finalize_database(self):
        if os.path.isfile(self.file_path):
            os.remove(self.file_path)
        os.rename(self.temp_file_path, self.file_path)

    async def _check_database_content(self):
        # load description
        try:
            self.description = await data.get_database_description(self.database)
            found_exchange_name = self.description[backtesting_enums.DataFormatKeys.EXCHANGE.value]
            found_symbols = [symbol_util.parse_symbol(symbol)
                             for symbol in self.description[backtesting_enums.DataFormatKeys.SYMBOLS.value]]
            found_time_frames = self.description[backtesting_enums.DataFormatKeys.TIME_FRAMES.value]
            if found_exchange_name != self.exchange_name:
                raise backtesting_errors.IncompatibleDatafileError(f"Exchange name in database: {found_exchange_name}, "
                                                                   f"requested exchange: {self.exchange_name}")
            if found_symbols != self.symbols:
                raise backtesting_errors.IncompatibleDatafileError(f"Pairs in database: {found_symbols}, "
                                                                   f"requested exchange: {self.symbols}")
            if found_time_frames != self.time_frames:
                raise backtesting_errors.IncompatibleDatafileError(f"Time frames name in database: {found_time_frames}, "
                                                                   f"requested exchange: {self.time_frames}")
        except commons_errors.DatabaseNotFoundError:
            # newly created datafile
            self.is_creating_database = True

    async def start(self):
        self.should_stop = False
        should_stop_database = True
        self.current_step_percent = 0
        self.total_steps = len(self.time_frames) * len(self.symbols)
        try:
            self.exchange_manager = trading_api.get_exchange_manager_from_exchange_id(self.exchange_id)

            # use a secondary exchange manager to fetch candles to fix ccxt pagination issues
            # seen on ccxt 4.1.82
            other_config = copy.copy(self.config)
            other_config[commons_constants.CONFIG_TIME_FRAME] = []   # any value here to avoid crashing
            self.fetch_exchange_manager = await trading_api.create_exchange_builder(other_config, self.exchange_name) \
                .is_simulated() \
                .is_rest_only() \
                .is_exchange_only() \
                .is_future(self.exchange_manager.is_future) \
                .disable_trading_mode() \
                .use_tentacles_setup_config(self.tentacles_setup_config) \
                .build()

            await self.adapt_timestamps()

            # create/update description
            if self.is_creating_database:
                await self._create_description()
            else:
                await self._update_description()

            self.in_progress = True

            self.logger.info(f"Start collecting history on {self.exchange_name}")
            tasks = []
            for symbol_index, symbol in enumerate(self.symbols):
                if symbol in self.missing_symbols:
                    self.logger.error(f"Skipping {symbol} from backtesting data: "
                                      f"missing price history on {self.exchange_name}")
                    continue
                self.logger.info(f"Collecting history for {symbol}...")
                tasks.append(asyncio.create_task(self.get_ticker_history(self.exchange_name, symbol)))
                tasks.append(asyncio.create_task(self.get_order_book_history(self.exchange_name, symbol)))
                tasks.append(asyncio.create_task(self.get_recent_trades_history(self.exchange_name, symbol)))

                for time_frame_index, time_frame in enumerate(self.time_frames):
                    tasks.append(asyncio.create_task(self.get_ohlcv_history(self.exchange_name, symbol, time_frame)))
                    tasks.append(asyncio.create_task(self.get_kline_history(self.exchange_name, symbol, time_frame)))
                    if symbol_index == time_frame_index == 0:
                        # let tables get created
                        await asyncio.gather(*tasks)
                        tasks = []
            if tasks:
                await asyncio.gather(*tasks)

        except Exception as err:
            await self.database.stop()
            should_stop_database = False
            # Do not keep errored data file
            if os.path.isfile(self.temp_file_path):
                os.remove(self.temp_file_path)
            if not self.should_stop:
                self.logger.exception(err, True, f"Error when collecting {self.exchange_name} history for "
                                                 f"{', '.join([symbol.symbol_str for symbol in self.symbols])}: {err}")
                raise backtesting_errors.DataCollectorError(err) from err
        finally:
            await self.stop(should_stop_database=should_stop_database)

    async def stop(self, should_stop_database=True):
        self.should_stop = True
        if should_stop_database:
            await self.database.stop()
            self.finalize_database()
        await self.fetch_exchange_manager.stop()
        self.exchange_manager = None
        self.in_progress = False
        self.finished = True
        return self.finished

    async def _update_description(self):
        updated_values = {}
        if self.end_timestamp and int(self.description[backtesting_enums.DataFormatKeys.END_TIMESTAMP.value]) * 1000 < self.end_timestamp:
            updated_values["end_timestamp"] = int(self.end_timestamp/1000)
        if self.start_timestamp and int(self.description[backtesting_enums.DataFormatKeys.START_TIMESTAMP.value]) * 1000 > self.start_timestamp:
            updated_values["start_timestamp"] = int(self.start_timestamp/1000)
        if updated_values:
            updated_values["timestamp"] = time.time()
            await self.database.update(backtesting_enums.DataTables.DESCRIPTION,
                                       updated_value_by_column=updated_values,
                                       version=self.VERSION,
                                       exchange=self.exchange_name,
                                       symbols=json.dumps([symbol.symbol_str for symbol in self.symbols]),
                                       time_frames=json.dumps([tf.value for tf in self.time_frames]))

    async def get_ticker_history(self, exchange, symbol):
        pass

    async def get_order_book_history(self, exchange, symbol):
        pass

    async def get_recent_trades_history(self, exchange, symbol):
        pass

    def get_ohlcv_snapshot(self, symbol, time_frame):
        symbol_data = trading_api.get_symbol_data(self.exchange_manager, str(symbol), allow_creation=False)
        candles = trading_api.get_symbol_historical_candles(symbol_data, time_frame)
        return [
            [
                time_val,
                candles[commons_enums.PriceIndexes.IND_PRICE_OPEN.value][index],
                candles[commons_enums.PriceIndexes.IND_PRICE_HIGH.value][index],
                candles[commons_enums.PriceIndexes.IND_PRICE_LOW.value][index],
                candles[commons_enums.PriceIndexes.IND_PRICE_CLOSE.value][index],
                candles[commons_enums.PriceIndexes.IND_PRICE_VOL.value][index],
            ]
            for index, time_val in enumerate(candles[commons_enums.PriceIndexes.IND_PRICE_TIME.value])
        ]

    async def collect_historical_ohlcv(self, exchange, symbol, time_frame, time_frame_sec,
                                       start_time, end_time, progress_multiplier):
        last_progress = 0
        symbol_id = str(symbol)
        async for candles in trading_api.get_historical_ohlcv(
            self.fetch_exchange_manager, symbol_id, time_frame, start_time, end_time
        ):
            await self.save_ohlcv(
                    exchange=exchange,
                    cryptocurrency=self.exchange_manager.exchange.get_pair_cryptocurrency(symbol_id),
                    symbol=symbol.symbol_str, time_frame=time_frame, candle=candles,
                    timestamp=[candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value] + time_frame_sec
                               for candle in candles],
                    multiple=True
            )
            progress = (candles[-1][commons_enums.PriceIndexes.IND_PRICE_TIME.value] - self.start_timestamp / 1000) / \
                                        ((self.end_timestamp - self.start_timestamp) / 1000) * 100
            progress_over_all_steps = progress * progress_multiplier / self.total_steps
            self.current_step_percent += progress_over_all_steps - last_progress
            self.logger.debug(f"progress: {self.current_step_percent}%")
            last_progress = progress_over_all_steps
        return last_progress

    def find_candle(self, candles, timestamp):
        for candle in candles:
            if candle[-1][commons_enums.PriceIndexes.IND_PRICE_TIME.value] == timestamp:
                return candle[-1], candle[0]
        return None, None

    async def update_ohlcv(self, exchange, symbol, time_frame, time_frame_sec,
                           database_candles, current_bot_candles):
        to_add_candles = []
        symbol_id = str(symbol)
        for up_to_date_candle in current_bot_candles:
            current_candle_time = up_to_date_candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value]
            equivalent_db_candle, candle_timestamp = self.find_candle(database_candles, current_candle_time)
            if equivalent_db_candle is None:
                to_add_candles.append(up_to_date_candle)
            elif equivalent_db_candle != up_to_date_candle:
                updated_value_by_column = {
                    "candle": json.dumps(up_to_date_candle)
                }
                await self.database.update(backtesting_enums.ExchangeDataTables.OHLCV,
                                           updated_value_by_column=updated_value_by_column,
                                           exchange_name=exchange,
                                           cryptocurrency=
                                           self.exchange_manager.exchange.get_pair_cryptocurrency(symbol_id),
                                           symbol=symbol.symbol_str,
                                           time_frame=time_frame.value,
                                           timestamp=str(candle_timestamp))
        if to_add_candles:
            await self.save_ohlcv(
                exchange=exchange,
                cryptocurrency=self.exchange_manager.exchange.get_pair_cryptocurrency(symbol_id),
                symbol=symbol, time_frame=time_frame, candle=to_add_candles,
                timestamp=[candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value] + time_frame_sec
                           for candle in to_add_candles],
                multiple=True
            )

    async def _check_ohlcv_integrity(self, database_candles):
        # ensure no timestamp is here twice
        all_timestamps = [candle[-1][0] for candle in database_candles]
        unique_timestamps = set(all_timestamps)
        if len(unique_timestamps) != len(database_candles):
            return {
                timestamp: counter
                for timestamp, counter in collections.Counter(all_timestamps).items()
                if counter > 1
            }
        return {}

    async def get_ohlcv_history(self, exchange, symbol, time_frame):
        try:
            last_progress = 0
            time_frame_sec = commons_enums.TimeFramesMinutes[time_frame] * commons_constants.MINUTE_TO_SECONDS
            # use current data from current bot
            fetch_data_id = self.get_fetch_data_id(symbol, time_frame)
            already_fetched_candles_candles = self.fetched_data[self.OHLCV][fetch_data_id]
            database_candles = []
            save_all_candles = self.is_creating_database
            updated_db = False
            if not self.is_creating_database:
                database_candles = await self._import_candles_from_datafile(exchange, symbol, time_frame)
                counters = await self._check_ohlcv_integrity(database_candles)
                if counters:
                    self.logger.warning(f"Duplicate candles in {exchange} data file for {symbol.symbol_str} "
                                        f"on {time_frame}. Problematic timestamps: {counters}. "
                                        f"Resetting database to ensure data integrity")

                    await self.delete_all(
                        backtesting_enums.ExchangeDataTables.OHLCV,
                        exchange=exchange,
                        cryptocurrency=self.exchange_manager.exchange.get_pair_cryptocurrency(str(symbol)),
                        symbol=symbol.symbol_str,
                        time_frame=time_frame
                    )
                    updated_db = True
                    save_all_candles = True
            if save_all_candles or not database_candles:
                await self.save_ohlcv(
                        exchange=exchange,
                        cryptocurrency=self.exchange_manager.exchange.get_pair_cryptocurrency(str(symbol)),
                        symbol=symbol.symbol_str, time_frame=time_frame, candle=already_fetched_candles_candles,
                        timestamp=[candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value] + time_frame_sec
                                   for candle in already_fetched_candles_candles],
                        multiple=True
                )
                database_candles = await self._import_candles_from_datafile(exchange, symbol, time_frame)
                updated_db = True
            candle_times = [
                candle[-1][commons_enums.PriceIndexes.IND_PRICE_TIME.value]
                for candle in database_candles
            ]
            # +/-1 not to fetch the last candle twice
            first_candle_data_time = min(candle_times) * 1000 - 1
            last_candle_data_time = max(candle_times) * 1000 + 1
            fill_before = self.start_timestamp and self.start_timestamp + time_frame_sec * 1000 < first_candle_data_time
            fill_after = last_candle_data_time < self.end_timestamp
            progress_per_collect = 0.5 if fill_after and fill_before else 1
            # 1. fill in any missing candle before existing candles
            if fill_before:
                # fetch missing data between required start time and actual start time in data file
                last_progress = await self.collect_historical_ohlcv(
                    exchange, symbol, time_frame, time_frame_sec, self.start_timestamp, first_candle_data_time,
                    progress_per_collect
                )
                if last_progress:
                    self.current_step_percent += 100 * progress_per_collect / self.total_steps - last_progress
                    updated_db = True
            # 2. fill in any missing candle after existing candles
            if fill_after:
                # fetch missing data between end time in data file and available data
                last_progress = await self.collect_historical_ohlcv(
                    exchange, symbol, time_frame, time_frame_sec, last_candle_data_time, self.end_timestamp,
                    progress_per_collect
                )
                if last_progress:
                    self.current_step_percent += 100 * progress_per_collect / self.total_steps - last_progress
                    updated_db = True
            if not (fill_before or fill_after):
                # nothing to collect, update progress still
                self.current_step_percent += 100 / self.total_steps
            if updated_db:
                database_candles = await self._import_candles_from_datafile(exchange, symbol, time_frame)
                counters = await self._check_ohlcv_integrity(database_candles)
                if counters:
                    self.logger.error(f"Error when checking database integrity of {exchange} "
                                      f"data file for {symbol.symbol_str}. "
                                      f"Delete this data file: {self.file_name} to reset it. "
                                      f"Problematic timestamps: {counters}")
        except Exception:
            raise

    async def _import_candles_from_datafile(self, exchange, symbol, time_frame):
        return importers.import_ohlcvs(
            await self.database.select(backtesting_enums.ExchangeDataTables.OHLCV,
                                       size=databases.SQLiteDatabase.DEFAULT_SIZE,
                                       exchange_name=exchange, symbol=symbol.symbol_str,
                                       time_frame=time_frame.value)
        )

    async def get_kline_history(self, exchange, symbol, time_frame):
        pass

    async def adapt_timestamps(self):
        lowest_timestamps = []
        for symbol in self.symbols:
            for tf in self.time_frames:
                first_timestamp = await self.get_first_candle_timestamp(
                    self.start_timestamp, symbol, tf
                )
                if first_timestamp is None:
                    self.missing_symbols.append(symbol)
                    break
                else:
                    lowest_timestamps.append(first_timestamp)
        lowest_timestamp = min(lowest_timestamps)
        # lowest_timestamp depends on self.start_timestamp if set. It will not go further
        if self.start_timestamp is None or lowest_timestamp < self.start_timestamp:
            self.start_timestamp = lowest_timestamp
        self.end_timestamp = self.end_timestamp or time.time() * 1000
        if self.start_timestamp > self.end_timestamp:
            raise backtesting_errors.DataCollectorError("start_timestamp is higher than end_timestamp")

    def get_fetch_data_id(self, symbol, timeframe):
        return f"{symbol}{timeframe.value}"

    async def get_first_candle_timestamp(self, ideal_start_timestamp, symbol, time_frame):
        try:
            symbol_data = trading_api.get_symbol_data(self.exchange_manager, str(symbol), allow_creation=False)
            candles = trading_api.get_symbol_historical_candles(symbol_data, time_frame)
            self.fetched_data[self.OHLCV][self.get_fetch_data_id(symbol, time_frame)] = self.get_ohlcv_snapshot(
                symbol, time_frame
            )
            return candles[commons_enums.PriceIndexes.IND_PRICE_TIME.value][0] * 1000
        except KeyError:
            # symbol or timeframe not available in live exchange
            fetched_candles = await self.fetch_exchange_manager.exchange.get_symbol_prices(
                str(symbol), time_frame, limit=1, since=ideal_start_timestamp
            )
            if not fetched_candles:
                return None
            self.fetched_data[self.OHLCV][self.get_fetch_data_id(symbol, time_frame)] = fetched_candles
            return fetched_candles[0][commons_enums.PriceIndexes.IND_PRICE_TIME.value] * 1000


================================================
FILE: Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["ExchangeBotSnapshotCollector"],
  "tentacles-requirements": []
}

================================================
FILE: Backtesting/collectors/exchanges/exchange_history_collector/__init__.py
================================================
from .history_collector import ExchangeHistoryDataCollector

================================================
FILE: Backtesting/collectors/exchanges/exchange_history_collector/history_collector.pxd
================================================
# cython: language_level=3
#  Drakkar-Software OctoBot-Backtesting
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.

from octobot_backtesting.collectors.exchanges.exchange_collector cimport AbstractExchangeHistoryCollector

cdef class ExchangeHistoryDataCollector(AbstractExchangeHistoryCollector):
    cdef public object exchange
    cdef public object exchange_manager


================================================
FILE: Backtesting/collectors/exchanges/exchange_history_collector/history_collector.py
================================================
#  Drakkar-Software OctoBot
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import logging
import os
import time

import octobot_backtesting.collectors as collector
import octobot_backtesting.enums as backtesting_enums
import octobot_backtesting.errors as errors
import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.time_frame_manager as time_frame_manager
import tentacles.Backtesting.importers.exchanges.generic_exchange_importer as generic_exchange_importer

try:
    import octobot_trading.api as trading_api
    import octobot_trading.enums as trading_enums
    import octobot_trading.errors as trading_errors
except ImportError:
    logging.error("ExchangeHistoryDataCollector requires OctoBot-Trading package installed")


class ExchangeHistoryDataCollector(collector.AbstractExchangeHistoryCollector):
    IMPORTER = generic_exchange_importer.GenericExchangeDataImporter

    def __init__(self, config, exchange_name, exchange_type, tentacles_setup_config, symbols, time_frames,
                 use_all_available_timeframes=False,
                 data_format=backtesting_enums.DataFormats.REGULAR_COLLECTOR_DATA,
                 start_timestamp=None,
                 end_timestamp=None):
        super().__init__(config, exchange_name, exchange_type, tentacles_setup_config, symbols, time_frames,
                         use_all_available_timeframes, data_format=data_format,
                         start_timestamp=start_timestamp, end_timestamp=end_timestamp)
        self.exchange = None
        self.exchange_manager = None

    async def start(self):
        self.should_stop = False
        should_stop_database = True
        try:
            use_future = self.exchange_type == trading_enums.ExchangeTypes.FUTURE
            self.exchange_manager = await trading_api.create_exchange_builder(self.config, self.exchange_name) \
                .is_simulated() \
                .is_rest_only() \
                .is_exchange_only() \
                .is_future(use_future) \
                .disable_trading_mode() \
                .use_tentacles_setup_config(self.tentacles_setup_config) \
                .build()

            self.exchange = self.exchange_manager.exchange
            self._load_timeframes_if_necessary()

            await self.check_timestamps()

            # create description
            await self._create_description()

            self.total_steps = len(self.time_frames) * len(self.symbols)
            self.in_progress = True

            self.logger.info(f"Start collecting history on {self.exchange_name}")
            for symbol_index, symbol in enumerate(self.symbols):
                self.logger.info(f"Collecting history for {symbol}...")
                await self.get_ticker_history(self.exchange_name, symbol)
                await self.get_order_book_history(self.exchange_name, symbol)
                await self.get_recent_trades_history(self.exchange_name, symbol)

                for time_frame_index, time_frame in enumerate(self.time_frames):
                    self.current_step_index = (symbol_index * len(self.time_frames)) + time_frame_index + 1
                    self.logger.info(
                        f"[{time_frame_index}/{len(self.time_frames)}] Collecting {symbol} history on {time_frame}...")
                    await self.get_ohlcv_history(self.exchange_name, symbol, time_frame)
                    await self.get_kline_history(self.exchange_name, symbol, time_frame)
        except Exception as err:
            await self.database.stop()
            should_stop_database = False
            # Do not keep errored data file
            if os.path.isfile(self.temp_file_path):
                os.remove(self.temp_file_path)
            if not self.should_stop:
                self.logger.exception(err, True, f"Error when collecting {self.exchange_name} history for "
                                                 f"{', '.join([str(symbol) for symbol in self.symbols])}: {err}")
                raise errors.DataCollectorError(err)
        finally:
            await self.stop(should_stop_database=should_stop_database)

    def _load_all_available_timeframes(self):
        allowed_timeframes = set(tf.value for tf in commons_enums.TimeFrames)
        self.time_frames = [commons_enums.TimeFrames(time_frame)
                            for time_frame in self.exchange_manager.client_time_frames
                            if time_frame in allowed_timeframes]

    async def stop(self, should_stop_database=True):
        self.should_stop = True
        if self.exchange_manager is not None:
            await self.exchange_manager.stop()
        if should_stop_database:
            await self.database.stop()
            self.finalize_database()
        self.exchange_manager = None
        self.in_progress = False
        self.finished = True
        return self.finished

    async def get_ticker_history(self, exchange, symbol):
        pass

    async def get_order_book_history(self, exchange, symbol):
        pass

    async def get_recent_trades_history(self, exchange, symbol):
        pass

    async def get_ohlcv_history(self, exchange, symbol, time_frame):
        self.current_step_percent = 0
        # use time_frame_sec to add time to save the candle closing time
        time_frame_sec = commons_enums.TimeFramesMinutes[time_frame] * commons_constants.MINUTE_TO_SECONDS
        symbol_id = str(symbol)
        cryptocurrency = self.exchange_manager.exchange.get_pair_cryptocurrency(symbol_id)
        if self.start_timestamp is not None:
            start_time = self.start_timestamp
            end_time = self.end_timestamp or time.time() * 1000
            first_candle_timestamp = await self.get_first_candle_timestamp(
                self.start_timestamp, symbol, time_frame
            ) * 1000
            if self.start_timestamp < first_candle_timestamp:
                start_time = first_candle_timestamp
            async for hist_candles in trading_api.get_historical_ohlcv(self.exchange_manager, symbol_id, time_frame,
                                                                       start_time, end_time):
                if hist_candles:
                    self.current_step_percent = \
                        (hist_candles[-1][commons_enums.PriceIndexes.IND_PRICE_TIME.value] - start_time / 1000) / \
                        ((end_time - start_time) / 1000) * 100
                    self.logger.info(f"[{self.current_step_percent}%] historical data fetched for {symbol} {time_frame}")
                    await self.save_ohlcv(
                        exchange=exchange,
                        cryptocurrency=cryptocurrency,
                        symbol=symbol.symbol_str, time_frame=time_frame, candle=hist_candles,
                        timestamp=[candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value] + time_frame_sec
                                   for candle in hist_candles],
                        multiple=True)
        else:
            try:
                candles = await self.exchange.get_symbol_prices(symbol_id, time_frame)
                if candles:
                    await self.save_ohlcv(exchange=exchange,
                                          cryptocurrency=cryptocurrency,
                                          symbol=symbol.symbol_str, time_frame=time_frame, candle=candles,
                                          timestamp=[candle[0] + time_frame_sec for candle in candles], multiple=True)
                else:
                    self.logger.error(f"No candles for {symbol} on {time_frame} ({exchange})")
            except trading_errors.FailedRequest as err:
                self.logger.exception(err, False)
                self.logger.warning(f"Ignored {symbol} {time_frame} candles on {exchange} ({err})")

    async def get_kline_history(self, exchange, symbol, time_frame):
        pass

    async def check_timestamps(self):
        if self.start_timestamp is not None:
            lowest_timestamp = min([
                await self.get_first_candle_timestamp(
                    self.start_timestamp, symbol, time_frame_manager.find_min_time_frame(self.time_frames)
                )
                for symbol in self.symbols
            ])
            if lowest_timestamp > self.start_timestamp:
                self.start_timestamp = lowest_timestamp
            if self.start_timestamp > (self.end_timestamp if self.end_timestamp else (time.time() * 1000)):
                raise errors.DataCollectorError("start_timestamp is higher than end_timestamp")

    async def get_first_candle_timestamp(self, ideal_start_timestamp, symbol, time_frame):
        try:
            return (
                await self.exchange.get_symbol_prices(str(symbol), time_frame, limit=1, since=ideal_start_timestamp)
            )[0][commons_enums.PriceIndexes.IND_PRICE_TIME.value]
        except (trading_errors.FailedRequest, IndexError) as err:
            raise errors.DataCollectorError(
                f"Impossible to initialize {self.exchange_name} data collector: {err}. This means that {symbol} "
                f"for the {time_frame.value} time frame is not supported in this context on {self.exchange_name}."
            )


================================================
FILE: Backtesting/collectors/exchanges/exchange_history_collector/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["ExchangeHistoryDataCollector"],
  "tentacles-requirements": []
}

================================================
FILE: Backtesting/collectors/exchanges/exchange_history_collector/tests/__init__.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.


================================================
FILE: Backtesting/collectors/exchanges/exchange_history_collector/tests/test_history_collector.py
================================================
#  Drakkar-Software OctoBot
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import pytest
import os
import contextlib
import json
import asyncio

import octobot_commons.databases as databases
import octobot_commons.symbols as commons_symbols
import octobot_commons.enums as commons_enums
import octobot_commons.constants as commons_constants
import octobot_backtesting.enums as enums
import octobot_backtesting.errors as errors
import octobot_trading.enums as trading_enums
import tests.test_utils.config as test_utils_config
import tentacles.Backtesting.collectors.exchanges as collector_exchanges
import tentacles.Trading.Exchange as tentacles_exchanges

# All test coroutines will be treated as marked.
pytestmark = pytest.mark.asyncio

BINANCEUS = "binanceus"
BINANCEUS_MAX_CANDLES_COUNT = 500


@contextlib.asynccontextmanager
async def data_collector(exchange_name, tentacles_setup_config, symbols, time_frames, use_all_available_timeframes,
                         start_timestamp=None, end_timestamp=None):
    collector_instance = collector_exchanges.ExchangeHistoryDataCollector(
        {}, exchange_name, trading_enums.ExchangeTypes.SPOT, tentacles_setup_config,
        [commons_symbols.parse_symbol(symbol) for symbol in symbols], time_frames,
        use_all_available_timeframes=use_all_available_timeframes,
        start_timestamp=start_timestamp,
        end_timestamp=end_timestamp
    )
    try:
        await collector_instance.initialize()
        yield collector_instance
    finally:
        if collector_instance.file_path and os.path.isfile(collector_instance.file_path):
            os.remove(collector_instance.file_path)
        if collector_instance.temp_file_path and os.path.isfile(collector_instance.temp_file_path):
            os.remove(collector_instance.temp_file_path)


@contextlib.asynccontextmanager
async def collector_database(collector):
    database = databases.SQLiteDatabase(collector.file_path)
    try:
        await database.initialize()
        yield database
    finally:
        await database.stop()


async def test_collect_valid_data():
    tentacles_setup_config = test_utils_config.load_test_tentacles_config()
    symbols = ["ETH/BTC"]
    async with data_collector(BINANCEUS, tentacles_setup_config, symbols, None, True) as collector:
        assert collector.time_frames == []
        assert collector.symbols == [commons_symbols.parse_symbol(symbol) for symbol in symbols]
        assert collector.exchange_name == BINANCEUS
        assert collector.tentacles_setup_config == tentacles_setup_config
        await collector.start()
        assert collector.time_frames != []
        assert collector.exchange_manager is None
        assert isinstance(collector.exchange, tentacles_exchanges.BinanceUS)
        assert collector.file_path is not None
        assert collector.temp_file_path is not None
        assert not os.path.isfile(collector.temp_file_path)
        assert os.path.isfile(collector.file_path)
        async with collector_database(collector) as database:
            ohlcv = await database.select(enums.ExchangeDataTables.OHLCV)
            # use > to take into account new possible candles since collect max time is not specified
            assert len(ohlcv) > 6000
            h_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, time_frame="1h")
            assert len(h_ohlcv) == BINANCEUS_MAX_CANDLES_COUNT
            eth_btc_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, symbol="ETH/BTC")
            assert len(eth_btc_ohlcv) == len(ohlcv)


async def test_collect_invalid_data():
    tentacles_setup_config = test_utils_config.load_test_tentacles_config()
    symbols = ["___ETH/BTC"]
    async with data_collector(BINANCEUS, tentacles_setup_config, symbols, None, True) as collector:
        with pytest.raises(errors.DataCollectorError):
            await collector.start()
        assert collector.time_frames != []
        assert collector.exchange_manager is None
        assert collector.exchange is not None
        assert collector.file_path is not None
        assert collector.temp_file_path is not None
        assert not os.path.isfile(collector.temp_file_path)


async def test_collect_valid_date_range():
    tentacles_setup_config = test_utils_config.load_test_tentacles_config()
    symbols = ["ETH/BTC"]
    start_time = 1569413160000
    end_time = 1569914160000
    # each request fetches 500 candles
    candle_fetch_limit = 500
    async with data_collector(BINANCEUS, tentacles_setup_config, symbols, None, True, start_time,
                              end_time) as collector:
        assert collector.start_timestamp is not None
        assert collector.end_timestamp is not None
        await collector.start()
        assert collector.time_frames != []
        assert collector.exchange_manager is None
        assert isinstance(collector.exchange, tentacles_exchanges.BinanceUS)
        assert collector.file_path is not None
        assert collector.temp_file_path is not None
        assert os.path.isfile(collector.file_path)
        assert not os.path.isfile(collector.temp_file_path)
        async with collector_database(collector) as database:
            ohlcv = await database.select(enums.ExchangeDataTables.OHLCV)
            assert len(ohlcv) == 13943
            parsed_candles = [
                json.loads(candle[-1])
                for candle in ohlcv
            ]
            for parsed_candle in parsed_candles:
                candle_open_time = parsed_candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value]
                assert start_time <= candle_open_time * 1000 <= end_time
            for time_frame in commons_enums.TimeFrames:
                time_frame_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, time_frame=time_frame.value)
                if not time_frame_ohlcv:
                    continue
                all_timestamps = sorted([
                    candle[commons_enums.PriceIndexes.IND_PRICE_TIME.value]
                    for candle in (
                        json.loads(candle[-1])
                        for candle in time_frame_ohlcv
                    )
                ])
                # ensure no duplicate
                timestamps = set(all_timestamps)
                assert len(timestamps) == len(time_frame_ohlcv)
                # ensure no missing
                interval = commons_enums.TimeFramesMinutes[time_frame] * commons_constants.MINUTE_TO_SECONDS
                current_ts = all_timestamps[0] - interval
                for timestamp in all_timestamps:
                    current_ts += interval
                    assert timestamp == current_ts

            h_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV,
                                            time_frame=commons_enums.TimeFrames.ONE_HOUR.value)
            assert len(h_ohlcv) == 139
            eth_btc_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, symbol="ETH/BTC")
            assert len(eth_btc_ohlcv) == len(ohlcv)
            min_timestamp = (await database.select_min(enums.ExchangeDataTables.OHLCV, ["timestamp"],
                                                       time_frame=commons_enums.TimeFrames.ONE_MINUTE.value))[0][
                                commons_enums.PriceIndexes.IND_PRICE_TIME.value] * 1000
            assert start_time <= min_timestamp <= start_time + (60 * 1000)
            max_timestamp = (await database.select_max(enums.ExchangeDataTables.OHLCV, ["timestamp"]))[0][
                                commons_enums.PriceIndexes.IND_PRICE_TIME.value] * 1000
            assert end_time <= max_timestamp <= end_time + (31 * 24 * 60 * 60 * 1000)


async def test_collect_invalid_date_range():
    tentacles_setup_config = test_utils_config.load_test_tentacles_config()
    symbols = ["ETH/BTC"]
    async with data_collector(BINANCEUS, tentacles_setup_config, symbols, None, True, 1609459200, 1577836800) \
            as collector:
        assert collector.start_timestamp is not None
        assert collector.end_timestamp is not None
        with pytest.raises(errors.DataCollectorError):
            await collector.start()
        assert collector.time_frames != []
        assert collector.exchange_manager is None
        assert isinstance(collector.exchange, tentacles_exchanges.BinanceUS)
        assert collector.file_path is not None
        assert collector.temp_file_path is not None
        assert not os.path.isfile(collector.file_path)
        assert not os.path.isfile(collector.temp_file_path)


async def test_collect_multi_pair():
    tentacles_setup_config = test_utils_config.load_test_tentacles_config()
    symbols = ["ETH/BTC", "BTC/USDT", "LTC/BTC"]
    async with data_collector(BINANCEUS, tentacles_setup_config, symbols, None, True) as collector:
        assert collector.time_frames == []
        assert collector.symbols == [commons_symbols.parse_symbol(symbol) for symbol in symbols]
        assert collector.exchange_name == BINANCEUS
        assert collector.tentacles_setup_config == tentacles_setup_config
        await collector.start()
        assert collector.time_frames != []
        assert collector.exchange_manager is None
        assert isinstance(collector.exchange, tentacles_exchanges.BinanceUS)
        assert collector.file_path is not None
        assert collector.temp_file_path is not None
        assert not os.path.isfile(collector.temp_file_path)
        assert os.path.isfile(collector.file_path)
        async with collector_database(collector) as database:
            ohlcv = await database.select(enums.ExchangeDataTables.OHLCV)
            # use > to take into account new possible candles since collect max time is not specified
            assert len(ohlcv) > 19316
            h_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, time_frame="4h")
            assert len(h_ohlcv) == len(symbols) * BINANCEUS_MAX_CANDLES_COUNT
            symbols_description = json.loads((await database.select(enums.DataTables.DESCRIPTION))[0][3])
            assert all(symbol in symbols_description for symbol in symbols)
            eth_btc_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, symbol="ETH/BTC")
            assert len(eth_btc_ohlcv) > 6598
            inch_btc_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, symbol="LTC/BTC")
            assert len(inch_btc_ohlcv) > 5803
            btc_usdt_ohlcv = await database.select(enums.ExchangeDataTables.OHLCV, symbol="BTC/USDT")
            assert len(btc_usdt_ohlcv) > 6598


async def test_stop_collect():
    tentacles_setup_config = test_utils_config.load_test_tentacles_config()
    symbols = ["AAVE/USDT"]
    async with data_collector(BINANCEUS, tentacles_setup_config, symbols, None, True, 1549065660000,
                              1632090006000) as collector:
        async def stop_soon():
            await asyncio.sleep(5)
            await collector.stop(should_stop_database=False)

        await asyncio.gather(collector.start(), stop_soon())
        assert collector.time_frames != []
        assert collector.symbols == [commons_symbols.parse_symbol(symbol) for symbol in symbols]
        assert collector.exchange_name == BINANCEUS
        assert collector.tentacles_setup_config == tentacles_setup_config
        assert collector.finished
        assert collector.exchange_manager is None
        assert not os.path.isfile(collector.temp_file_path)
        assert not os.path.isfile(collector.file_path)


================================================
FILE: Backtesting/collectors/exchanges/exchange_live_collector/__init__.py
================================================
from .live_collector import ExchangeLiveDataCollector

================================================
FILE: Backtesting/collectors/exchanges/exchange_live_collector/live_collector.pxd
================================================
# cython: language_level=3
#  Drakkar-Software OctoBot-Backtesting
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.

from octobot_backtesting.collectors.exchanges.exchange_collector cimport ExchangeDataCollector

cdef class ExchangeLiveDataCollector(ExchangeDataCollector):
    pass


================================================
FILE: Backtesting/collectors/exchanges/exchange_live_collector/live_collector.py
================================================
#  Drakkar-Software OctoBot
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import asyncio
import logging
import time

import octobot_backtesting.collectors.exchanges as exchanges
import octobot_commons.channels_name as channels_name
import tentacles.Backtesting.importers.exchanges.generic_exchange_importer as generic_exchange_importer

try:
    import octobot_trading.exchange_channel as exchange_channel
    import octobot_trading.api as trading_api
except ImportError:
    logging.error("ExchangeLiveDataCollector requires OctoBot-Trading package installed")


class ExchangeLiveDataCollector(exchanges.AbstractExchangeLiveCollector):
    IMPORTER = generic_exchange_importer.GenericExchangeDataImporter

    async def start(self):
        exchange_manager = await trading_api.create_exchange_builder(self.config, self.exchange_name) \
            .is_simulated() \
            .is_rest_only() \
            .is_without_auth() \
            .is_ignoring_config() \
            .disable_trading_mode() \
            .use_tentacles_setup_config(self.tentacles_setup_config) \
            .build()

        self._load_timeframes_if_necessary()

        # create description
        await self._create_description()

        exchange_id = exchange_manager.id
        await exchange_channel.get_chan(channels_name.OctoBotTradingChannelsName.TICKER_CHANNEL.value,
                                        exchange_id).new_consumer(self.ticker_callback)
        await exchange_channel.get_chan(channels_name.OctoBotTradingChannelsName.RECENT_TRADES_CHANNEL.value,
                                        exchange_id).new_consumer(self.recent_trades_callback)
        await exchange_channel.get_chan(channels_name.OctoBotTradingChannelsName.ORDER_BOOK_CHANNEL.value,
                                        exchange_id).new_consumer(self.order_book_callback)
        await exchange_channel.get_chan(channels_name.OctoBotTradingChannelsName.KLINE_CHANNEL.value,
                                        exchange_id).new_consumer(self.kline_callback)
        await exchange_channel.get_chan(channels_name.OctoBotTradingChannelsName.OHLCV_CHANNEL.value,
                                        exchange_id).new_consumer(self.ohlcv_callback)

        await asyncio.gather(*asyncio.all_tasks(asyncio.get_event_loop()))

    async def ticker_callback(self, exchange: str, exchange_id: str,
                              cryptocurrency: str, symbol: str, ticker):
        self.logger.info(f"TICKER : CRYPTOCURRENCY = {cryptocurrency} || SYMBOL = {symbol} || TICKER = {ticker}")
        await self.save_ticker(timestamp=time.time(), exchange=exchange,
                               cryptocurrency=cryptocurrency, symbol=symbol, ticker=ticker)

    async def order_book_callback(self, exchange: str, exchange_id: str,
                                  cryptocurrency: str, symbol: str, asks, bids):
        self.logger.info(f"ORDERBOOK : CRYPTOCURRENCY = {cryptocurrency} || SYMBOL = {symbol} "
                         f"|| ASKS = {asks} || BIDS = {bids}")
        await self.save_order_book(timestamp=time.time(), exchange=exchange,
                                   cryptocurrency=cryptocurrency, symbol=symbol, asks=asks, bids=bids)

    async def recent_trades_callback(self, exchange: str, exchange_id: str,
                                     cryptocurrency: str, symbol: str, recent_trades):
        self.logger.info(f"RECENT TRADE : CRYPTOCURRENCY = {cryptocurrency} || SYMBOL = {symbol} "
                         f"|| RECENT TRADE = {recent_trades}")
        await self.save_recent_trades(timestamp=time.time(), exchange=exchange,
                                      cryptocurrency=cryptocurrency, symbol=symbol, recent_trades=recent_trades)

    async def ohlcv_callback(self, exchange: str, exchange_id: str,
                             cryptocurrency: str, symbol: str, time_frame, candle):
        self.logger.info(f"OHLCV : CRYPTOCURRENCY = {cryptocurrency} || SYMBOL = {symbol} "
                         f"|| TIME FRAME = {time_frame} || CANDLE = {candle}")
        await self.save_ohlcv(timestamp=time.time(), exchange=exchange,
                              cryptocurrency=cryptocurrency, symbol=symbol, time_frame=time_frame, candle=candle)

    async def kline_callback(self, exchange: str, exchange_id: str,
                             cryptocurrency: str, symbol: str, time_frame, kline):
        self.logger.info(f"KLINE : CRYPTOCURRENCY = {cryptocurrency} || SYMBOL = {symbol} "
                         f"|| TIME FRAME = {time_frame} || KLINE = {kline}")
        await self.save_kline(timestamp=time.time(), exchange=exchange,
                              cryptocurrency=cryptocurrency, symbol=symbol, time_frame=time_frame, kline=kline)


================================================
FILE: Backtesting/collectors/exchanges/exchange_live_collector/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["ExchangeLiveDataCollector"],
  "tentacles-requirements": []
}

================================================
FILE: Backtesting/converters/exchanges/legacy_data_converter/__init__.py
================================================
from .legacy_converter import LegacyDataConverter

================================================
FILE: Backtesting/converters/exchanges/legacy_data_converter/legacy_converter.pxd
================================================
# cython: language_level=3
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
from octobot_backtesting.converters.data_converter cimport DataConverter
from octobot_backtesting.data.database cimport DataBase

cdef class LegacyDataConverter(DataConverter):
    cdef str exchange_name
    cdef str symbol
    cdef str time_data
    cdef list time_frames
    cdef dict file_content
    cdef DataBase database

    cdef list _get_formatted_candles(self, object time_frame)
    cdef dict _read_data_file(self)
    cdef dict _read_data_file(self)


================================================
FILE: Backtesting/converters/exchanges/legacy_data_converter/legacy_converter.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import gzip
import json
import enum
import os.path as path
import datetime

import octobot_backtesting.collectors.exchanges as exchanges
import octobot_backtesting.constants as backtesting_constants
import octobot_backtesting.converters as converters
import octobot_backtesting.data as backtesting_data
import octobot_backtesting.enums as backtesting_enums
import octobot_commons.databases as databases
import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.symbols.symbol_util as symbol_util


class LegacyDataConverter(converters.DataConverter):
    """
    LegacyDataConverter can be used to convert OctoBot v0.3 data files into v0.4 data files.
    """
    DATA_FILE_EXT = ".data"
    VERSION = "1.0"
    DATA_FILE_TIME_DATE_FORMAT = '%Y%m%d%H%M%S'

    class PriceIndexes(enum.Enum):
        IND_PRICE_TIME = 0
        IND_PRICE_OPEN = 1
        IND_PRICE_HIGH = 2
        IND_PRICE_LOW = 3
        IND_PRICE_CLOSE = 4
        IND_PRICE_VOL = 5

    def __init__(self, backtesting_file_to_convert):
        super().__init__(backtesting_file_to_convert)
        self.exchange_name = ""
        self.symbol = ""
        self.time_data = ""
        self.time_frames = []
        self.file_content = {}
        self.database = None
        self.converted_file = backtesting_data.get_backtesting_file_name(exchanges.AbstractExchangeHistoryCollector)

    async def can_convert(self, ) -> bool:
        self.exchange_name, self.symbol, self.time_data = LegacyDataConverter._interpret_file_name(self.file_to_convert)
        if None in (self.exchange_name, self.symbol, self.time_data):
            return False
        self.file_content = self._read_data_file()
        if not self.file_content:
            return False
        for time_frame, candles_data in self.file_content.items():
            try:
                # check time frame validity
                time_frame = commons_enums.TimeFrames(time_frame)
                # check candle data validity
                if isinstance(candles_data, list) and len(candles_data) == 6:
                    # check candle data non-emptiness
                    if all(data for data in candles_data):
                        self.time_frames.append(time_frame)
            except ValueError:
                pass
        return bool(self.time_frames)

    async def convert(self) -> bool:
        try:
            self.database = databases.SQLiteDatabase(
                path.join(backtesting_constants.BACKTESTING_FILE_PATH, self.converted_file))
            await self.database.initialize()
            await self._create_description()
            for time_frame in self.time_frames:
                await self._convert_ohlcv(time_frame)
            return True
        except Exception as e:
            self.logger.exception(e, True, f"Error while converting data file: {e}")
            return False
        finally:
            if self.database is not None:
                await self.database.stop()

    async def _create_description(self):
        time_object = datetime.datetime.strptime(self.time_data, self.DATA_FILE_TIME_DATE_FORMAT)
        await self.database.insert(backtesting_enums.DataTables.DESCRIPTION,
                                   timestamp=datetime.datetime.timestamp(time_object),
                                   version=self.VERSION,
                                   exchange=self.exchange_name,
                                   symbols=json.dumps([self.symbol]),
                                   time_frames=json.dumps([tf.value for tf in self.time_frames]))

    async def _convert_ohlcv(self, time_frame):
        # use time_frame_sec to add time to save the candle closing time
        time_frame_sec = commons_enums.TimeFramesMinutes[time_frame] * commons_constants.MINUTE_TO_SECONDS
        candles = self._get_formatted_candles(time_frame)
        await self.database.insert_all(backtesting_enums.ExchangeDataTables.OHLCV,
                                       timestamp=[candle[0] + time_frame_sec for candle in candles],
                                       exchange_name=self.exchange_name, symbol=self.symbol,
                                       time_frame=time_frame.value, candle=[json.dumps(c) for c in candles])

    def _get_formatted_candles(self, time_frame):
        data = self.file_content[time_frame.value]
        candles = []
        for i in range(len(data[LegacyDataConverter.PriceIndexes.IND_PRICE_TIME.value])):
            candles.insert(i, [None] * len(LegacyDataConverter.PriceIndexes))
            candles[i][LegacyDataConverter.PriceIndexes.IND_PRICE_CLOSE.value] = \
                data[LegacyDataConverter.PriceIndexes.IND_PRICE_CLOSE.value][i]
            candles[i][LegacyDataConverter.PriceIndexes.IND_PRICE_OPEN.value] = \
                data[LegacyDataConverter.PriceIndexes.IND_PRICE_OPEN.value][i]
            candles[i][LegacyDataConverter.PriceIndexes.IND_PRICE_HIGH.value] = \
                data[LegacyDataConverter.PriceIndexes.IND_PRICE_HIGH.value][i]
            candles[i][LegacyDataConverter.PriceIndexes.IND_PRICE_LOW.value] = \
                data[LegacyDataConverter.PriceIndexes.IND_PRICE_LOW.value][i]
            candles[i][LegacyDataConverter.PriceIndexes.IND_PRICE_TIME.value] = \
                data[LegacyDataConverter.PriceIndexes.IND_PRICE_TIME.value][i]
            candles[i][LegacyDataConverter.PriceIndexes.IND_PRICE_VOL.value] = \
                data[LegacyDataConverter.PriceIndexes.IND_PRICE_VOL.value][i]
        return candles

    def _read_data_file(self):
        try:
            # try zipfile
            with gzip.open(self.file_to_convert, 'r') as file_to_parse:
                file_content = json.loads(file_to_parse.read())
        except OSError:
            # try without unzip
            with open(self.file_to_convert) as file_to_parse:
                file_content = json.loads(file_to_parse.read())
        except Exception:
            return {}
        return file_content

    @staticmethod
    def _interpret_file_name(file_name):
        data = path.basename(file_name).split("_")
        try:
            exchange_name = data[0]
            symbol = symbol_util.merge_currencies(data[1], data[2])
            file_ext = LegacyDataConverter.DATA_FILE_EXT
            timestamp = data[3] + data[4].replace(file_ext, "")
        except KeyError:
            exchange_name = None
            symbol = None
            timestamp = None

        return exchange_name, symbol, timestamp


================================================
FILE: Backtesting/converters/exchanges/legacy_data_converter/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["LegacyDataConverter"],
  "tentacles-requirements": []
}

================================================
FILE: Backtesting/importers/exchanges/generic_exchange_importer/__init__.py
================================================
from .generic_exchange_importer import GenericExchangeDataImporter

================================================
FILE: Backtesting/importers/exchanges/generic_exchange_importer/generic_exchange_importer.pxd
================================================
# cython: language_level=3
#  Drakkar-Software OctoBot-Backtesting
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
from octobot_backtesting.importers.exchanges.exchange_importer cimport ExchangeDataImporter

cdef class GenericExchangeDataImporter(ExchangeDataImporter):
    pass

================================================
FILE: Backtesting/importers/exchanges/generic_exchange_importer/generic_exchange_importer.py
================================================
#  Drakkar-Software OctoBot-Backtesting
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import octobot_backtesting.importers as importers


class GenericExchangeDataImporter(importers.ExchangeDataImporter):
    pass


================================================
FILE: Backtesting/importers/exchanges/generic_exchange_importer/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["GenericExchangeDataImporter"],
  "tentacles-requirements": []
}

================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/__init__.py
================================================
from .instant_fluctuations import InstantFluctuationsEvaluator, InstantMAEvaluator

================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/config/InstantFluctuationsEvaluator.json
================================================
{
    "price_difference_threshold_percent": 1,
    "volume_difference_threshold_percent": 400,
    "time_frame": "1m"
}

================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/config/InstantMAEvaluator.json
================================================
{
    "period": 6,
    "time_frame": "1m",
    "threshold": 0.5
}

================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/instant_fluctuations.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import math
import tulipy
import numpy as np

import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.channels_name as channels_name
import octobot_evaluators.evaluators as evaluators
import octobot_evaluators.util as evaluators_util


class InstantFluctuationsEvaluator(evaluators.RealTimeEvaluator):
    """
    Idea: moves are lasting approx 12min
    Check the last 12 candles and compute mean closing prices as
    well as mean volume with a gradually narrower interval to
    compute the strength or weakness of the move
    """

    PRICE_THRESHOLD_KEY = "price_difference_threshold_percent"
    VOLUME_THRESHOLD_KEY = "volume_difference_threshold_percent"

    def __init__(self, tentacles_setup_config):
        super().__init__(tentacles_setup_config)
        self.something_is_happening = False
        self.last_notification_eval = 0

        self.average_prices = {}
        self.last_price = 0

        # Volume
        self.average_volumes = {}
        self.last_volume = 0

        # Constants
        self.time_frame = None
        self.VOLUME_HAPPENING_THRESHOLD = None
        self.PRICE_HAPPENING_THRESHOLD = None
        self.MIN_TRIGGERING_DELTA = 0.15
        self.candle_segments = [10, 8, 6, 5, 4, 3, 2, 1]

    def init_user_inputs(self, inputs: dict) -> None:
        """
        Called right before starting the tentacle, should define all the tentacle's user inputs unless
        those are defined somewhere else.
        """
        self.time_frame = self.time_frame or \
            self.UI.user_input(commons_constants.CONFIG_TIME_FRAME, commons_enums.UserInputTypes.OPTIONS,
                               commons_enums.TimeFrames.ONE_MINUTE.value,
                               inputs, options=[tf.value for tf in commons_enums.TimeFrames],
                               title="Time frame: The time frame to observe in order to spot changes.")
        self.VOLUME_HAPPENING_THRESHOLD = 1 + self.UI.user_input(
            self.VOLUME_THRESHOLD_KEY, commons_enums.UserInputTypes.FLOAT, 400, inputs, min_val=0,
            title="Volume threshold: volume difference in percent from which to trigger a notification."
        ) / 100
        self.PRICE_HAPPENING_THRESHOLD = self.UI.user_input(
            self.PRICE_THRESHOLD_KEY, commons_enums.UserInputTypes.FLOAT, 1, inputs, min_val=0,
            title="Price threshold: price difference in percent from which to trigger a notification."
        ) / 100

    async def ohlcv_callback(self, exchange: str, exchange_id: str,
                             cryptocurrency: str, symbol: str, time_frame, candle):
        volume_data = self.get_symbol_candles(exchange, exchange_id, symbol, time_frame). \
            get_symbol_volume_candles(self.candle_segments[0])
        close_data = self.get_symbol_candles(exchange, exchange_id, symbol, time_frame). \
            get_symbol_close_candles(self.candle_segments[0])
        for segment in self.candle_segments:
            volume_data = [d for d in volume_data[-segment:] if d is not None]
            price_data = [d for d in close_data[-segment:] if d is not None]
            self.average_volumes[segment] = np.mean(volume_data)
            self.average_prices[segment] = np.mean(price_data)

        try:
            self.last_volume = volume_data[-1]
            self.last_price = close_data[-1]
            await self._trigger_evaluation(cryptocurrency, symbol,
                                           evaluators_util.get_eval_time(full_candle=candle, time_frame=time_frame))
        except IndexError:
            # candles data history is probably not yet available
            self.logger.debug(f"Impossible to evaluate, no historical data for {symbol} on {time_frame}")

    async def kline_callback(self, exchange: str, exchange_id: str,
                             cryptocurrency: str, symbol: str, time_frame, kline):
        self.last_volume = kline[commons_enums.PriceIndexes.IND_PRICE_VOL.value]
        self.last_price = kline[commons_enums.PriceIndexes.IND_PRICE_CLOSE.value]
        await self._trigger_evaluation(cryptocurrency, symbol, evaluators_util.get_eval_time(kline=kline))

    async def _trigger_evaluation(self, cryptocurrency, symbol, time):
        self.evaluate_volume_fluctuations()
        if self.something_is_happening and self.eval_note != commons_constants.START_PENDING_EVAL_NOTE:
            if abs(self.last_notification_eval - self.eval_note) >= self.MIN_TRIGGERING_DELTA:
                self.last_notification_eval = self.eval_note
                await self.evaluation_completed(cryptocurrency, symbol, self.available_time_frame,
                                                eval_time=time)
            self.something_is_happening = False
        else:
            self.eval_note = commons_constants.START_PENDING_EVAL_NOTE

    def evaluate_volume_fluctuations(self):
        volume_trigger = 0
        price_trigger = 0

        for segment in self.candle_segments:
            if segment in self.average_volumes and segment in self.average_prices:
                # check volume fluctuation
                if self.last_volume > self.VOLUME_HAPPENING_THRESHOLD * self.average_volumes[segment]:
                    volume_trigger += 1
                    self.something_is_happening = True

                # check price fluctuation
                segment_average_price = self.average_prices[segment]
                if self.last_price > (1 + self.PRICE_HAPPENING_THRESHOLD) * segment_average_price:
                    price_trigger += 1
                    self.something_is_happening = True

                elif self.last_price < (1 - self.PRICE_HAPPENING_THRESHOLD) * segment_average_price:
                    price_trigger -= 1
                    self.something_is_happening = True

        if self.candle_segments:
            average_volume_trigger = min(1, volume_trigger / len(self.candle_segments) + 0.2)
            average_price_trigger = price_trigger / len(self.candle_segments)

            if average_price_trigger < 0:
                # math.cos(1-x) between 0 and 1 starts around 0.5 and smoothly goes up to 1
                self.eval_note = -1 * math.cos(1 - (-1 * average_price_trigger * average_volume_trigger))
            elif average_price_trigger > 0:
                self.eval_note = math.cos(1 - average_price_trigger * average_volume_trigger)
            else:
                # no price info => high volume but no price move, can't say anything
                self.something_is_happening = False
        else:
            self.something_is_happening = False

    async def start(self, bot_id: str) -> bool:
        """
        Subscribe to Kline and OHLCV notification
        :return: bool
        """
        try:
            import octobot_trading.exchange_channel as exchange_channels
            import octobot_trading.api as trading_api
            exchange_id = trading_api.get_exchange_id_from_matrix_id(self.exchange_name, self.matrix_id)
            await exchange_channels.get_chan(channels_name.OctoBotTradingChannelsName.OHLCV_CHANNEL.value,
                                             exchange_id).new_consumer(
                callback=self.ohlcv_callback, symbol=self.symbol,
                time_frame=self.available_time_frame, priority_level=self.priority_level)
            await exchange_channels.get_chan(channels_name.OctoBotTradingChannelsName.KLINE_CHANNEL.value,
                                             exchange_id).new_consumer(
                callback=self.kline_callback, symbol=self.symbol,
                time_frame=self.available_time_frame, priority_level=self.priority_level)
            return True
        except ImportError:
            self.logger.error("Can't connect to trading channels")
        return False

    def set_default_config(self):
        super().set_default_config()
        self.specific_config[commons_constants.CONFIG_TIME_FRAME] = "1m"

    @classmethod
    def get_is_symbol_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not symbol dependant else False
        """
        return False


class InstantMAEvaluator(evaluators.RealTimeEvaluator):

    def __init__(self, tentacles_setup_config):
        super().__init__(tentacles_setup_config)
        self.last_candle_data = {}
        self.last_moving_average_values = {}
        self.period = 6
        self.time_frame = None
        self.price_threshold = 0.05

    def init_user_inputs(self, inputs: dict) -> None:
        """
        Called right before starting the tentacle, should define all the tentacle's user inputs unless
        those are defined somewhere else.
        """
        self.time_frame = self.time_frame or \
            self.UI.user_input(commons_constants.CONFIG_TIME_FRAME, commons_enums.UserInputTypes.OPTIONS,
                               commons_enums.TimeFrames.ONE_MINUTE.value,
                               inputs, options=[tf.value for tf in commons_enums.TimeFrames],
                               title="Time frame: The time frame to observe in order to spot changes.")
        self.period = self.UI.user_input("period", commons_enums.UserInputTypes.INT, 6, inputs,
                                         min_val=1, title="Period: the EMA period length to use.")
        self.price_threshold = self.UI.user_input(
            "threshold", commons_enums.UserInputTypes.FLOAT, self.price_threshold * 100, inputs, min_val=0,
            title="Price threshold: price difference in percent from the current moving average value starting "
                  "from which to trigger an evaluation."
        ) / 100

    async def ohlcv_callback(self, exchange: str, exchange_id: str,
                             cryptocurrency: str, symbol: str, time_frame, candle):
        self.eval_note = 0
        new_data = self.get_symbol_candles(exchange, exchange_id, symbol, time_frame). \
            get_symbol_close_candles(20)
        should_eval = symbol not in self.last_candle_data or \
                      not self._compare_data(new_data, self.last_candle_data[symbol])
        self.last_candle_data[symbol] = new_data
        if should_eval:
            if len(self.last_candle_data[symbol]) > self.period:
                self.last_moving_average_values[symbol] = tulipy.sma(self.last_candle_data[symbol],
                                                                     self.period)
                await self._evaluate_current_price(self.last_candle_data[symbol][-1], cryptocurrency, symbol,
                                                   evaluators_util.get_eval_time(full_candle=candle,
                                                                                 time_frame=time_frame))

    async def kline_callback(self, exchange: str, exchange_id: str,
                             cryptocurrency: str, symbol: str, time_frame, kline):
        if symbol in self.last_moving_average_values and len(self.last_moving_average_values[symbol]) > 0:
            self.eval_note = 0
            last_price = kline[commons_enums.PriceIndexes.IND_PRICE_CLOSE.value]
            if last_price != self.last_candle_data[symbol][-1]:
                await self._evaluate_current_price(last_price, cryptocurrency, symbol,
                                                   evaluators_util.get_eval_time(kline=kline))

    async def _evaluate_current_price(self, last_price, cryptocurrency, symbol, time):
        last_ma_value = self.last_moving_average_values[symbol][-1]
        if last_ma_value == 0:
            self.eval_note = 0
        else:
            lower_threshold = last_ma_value * (1 - self.price_threshold)
            upper_threshold = last_ma_value * (1 + self.price_threshold)
            if lower_threshold < last_price < upper_threshold:
                self.eval_note = 0
            else:
                current_ratio = last_price / last_ma_value
                if current_ratio > 1:
                    # last_price > last_ma_value => sell ? => eval_note > 0
                    if current_ratio >= 2:
                        self.eval_note = 1
                    else:
                        self.eval_note = current_ratio - 1
                elif current_ratio < 1:
                    # last_price < last_ma_value => buy ? => eval_note < 0
                    self.eval_note = -1 * (1 - current_ratio)
                else:
                    self.eval_note = 0

        await self.evaluation_completed(cryptocurrency, symbol, self.available_time_frame,
                                        eval_time=time)

    async def start(self, bot_id: str) -> bool:
        """
        Subscribe to Kline and OHLCV notification
        :return: bool
        """
        try:
            import octobot_trading.exchange_channel as exchange_channels
            import octobot_trading.api as trading_api
            exchange_id = trading_api.get_exchange_id_from_matrix_id(self.exchange_name, self.matrix_id)
            await exchange_channels.get_chan(channels_name.OctoBotTradingChannelsName.OHLCV_CHANNEL.value,
                                             exchange_id).new_consumer(
                callback=self.ohlcv_callback, time_frame=self.available_time_frame, priority_level=self.priority_level)
            await exchange_channels.get_chan(channels_name.OctoBotTradingChannelsName.KLINE_CHANNEL.value,
                                             exchange_id).new_consumer(
                callback=self.kline_callback, time_frame=self.available_time_frame, priority_level=self.priority_level)
            return True
        except ImportError:
            self.logger.error("Can't connect to trading channels")
        return False

    def set_default_config(self):
        super().set_default_config()
        self.specific_config[commons_constants.CONFIG_TIME_FRAME] = "1m"

    @staticmethod
    def _compare_data(new_data, old_data):
        try:
            if new_data[commons_enums.PriceIndexes.IND_PRICE_CLOSE.value][-1] != \
                    old_data[commons_enums.PriceIndexes.IND_PRICE_CLOSE.value][-1]:
                return False
            return True
        except Exception:
            return False


================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["InstantFluctuationsEvaluator", "InstantMAEvaluator"],
  "tentacles-requirements": []
}

================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/resources/InstantFluctuationsEvaluator.md
================================================
Triggers when a superior to 1% change of price or a superior to x4 change of volume from recent average happens.

The price distance from recent average is defining the strength the evaluation.

================================================
FILE: Evaluator/RealTime/instant_fluctuations_evaluator/resources/InstantMAEvaluator.md
================================================
Uses a [moving average](https://www.investopedia.com/terms/m/movingaverage.asp) 
computed on close prices to set its evaluation. 

Will trigger an evaluation when the current close price is beyond the given price threshold applied on
the latest moving average value.

Triggers on each new candle and price change. 


================================================
FILE: Evaluator/Social/forum_evaluator/__init__.py
================================================
from .forum import RedditForumEvaluator

================================================
FILE: Evaluator/Social/forum_evaluator/config/RedditForumEvaluator.json
================================================
{
    "crypto-currencies": [
        {
            "crypto-currency": "Bitcoin",
            "subreddits": [
                "Bitcoin"
            ]
        },
        {
            "crypto-currency": "Ethereum",
            "subreddits": [
                "ethereum"
            ]
        },
        {
            "crypto-currency": "NEO",
            "subreddits": [
                "NEO"
            ]
        },
        {
            "crypto-currency": "ICON",
            "subreddits": [
                "icon"
            ]
        },
        {
            "crypto-currency": "NANO",
            "subreddits": [
                "nanocurrency"
            ]
        },
        {
            "crypto-currency": "VeChain",
            "subreddits": [
                "Vechain"
            ]
        },
        {
            "crypto-currency": "VeChain Thor",
            "subreddits": [
                "Vechain"
            ]
        },
        {
            "crypto-currency": "Substratum",
            "subreddits": [
                "SubstratumNetwork"
            ]
        },
        {
            "crypto-currency": "Ethos",
            "subreddits": [
                "ethos_io"
            ]
        },
        {
            "crypto-currency": "Ontology",
            "subreddits": [
                "OntologyNetwork"
            ]
        },
        {
            "crypto-currency": "Binance Coin",
            "subreddits": []
        }
    ]
}

================================================
FILE: Evaluator/Social/forum_evaluator/forum.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.tentacles_management as tentacles_management
import octobot_evaluators.evaluators as evaluators
import octobot_services.constants as services_constants
import tentacles.Services.Services_feeds as Services_feeds
import tentacles.Evaluator.Util as EvaluatorUtil

CONFIG_REDDIT = "reddit"
CONFIG_REDDIT_SUBREDDITS = "subreddits"
CONFIG_REDDIT_ENTRY = "entry"
CONFIG_REDDIT_ENTRY_WEIGHT = "entry_weight"


# RedditForumEvaluator is used to get an overall state of a market, it will not trigger a trade
# (notify its evaluators) but is used to measure hype and trend of a market.
class RedditForumEvaluator(evaluators.SocialEvaluator):
    SERVICE_FEED_CLASS = Services_feeds.RedditServiceFeed if hasattr(Services_feeds, 'RedditServiceFeed') else None

    def __init__(self, tentacles_setup_config):
        evaluators.SocialEvaluator.__init__(self, tentacles_setup_config)
        self.overall_state_analyser = EvaluatorUtil.OverallStateAnalyser()
        self.count = 0
        self.sentiment_analyser = None
        self.is_self_refreshing = True
        self.subreddits_by_cryptocurrency = {}

    def init_user_inputs(self, inputs: dict) -> None:
        """
        Called right before starting the tentacle, should define all the tentacle's user inputs unless
        those are defined somewhere else.
        """
        cryptocurrencies = []
        config_cryptocurrencies = self.UI.user_input(
            commons_constants.CONFIG_CRYPTO_CURRENCIES, commons_enums.UserInputTypes.OBJECT_ARRAY,
            cryptocurrencies, inputs, other_schema_values={"minItems": 1, "uniqueItems": True},
            item_title="Crypto currency",
            title="Crypto currencies to watch."
        )
        # init one user input to generate user input schema and default values
        cryptocurrencies.append(self._init_cryptocurrencies(inputs, "Bitcoin", ["Bitcoin"]))
        # remove other symbols data to avoid unnecessary entries
        self.subreddits_by_cryptocurrency = self._get_config_elements(config_cryptocurrencies, CONFIG_REDDIT_SUBREDDITS)
        self.feed_config[services_constants.CONFIG_REDDIT_SUBREDDITS] = self.subreddits_by_cryptocurrency

    def _init_cryptocurrencies(self, inputs, cryptocurrency, subreddits):
        return {
            commons_constants.CONFIG_CRYPTO_CURRENCY:
                self.UI.user_input(commons_constants.CONFIG_CRYPTO_CURRENCY, commons_enums.UserInputTypes.TEXT,
                                cryptocurrency, inputs, other_schema_values={"minLength": 2},
                                parent_input_name=commons_constants.CONFIG_CRYPTO_CURRENCIES, array_indexes=[0],
                                title="Crypto currency name"),
            CONFIG_REDDIT_SUBREDDITS:
                self.UI.user_input(CONFIG_REDDIT_SUBREDDITS, commons_enums.UserInputTypes.STRING_ARRAY,
                                subreddits, inputs, other_schema_values={"uniqueItems": True},
                                parent_input_name=commons_constants.CONFIG_CRYPTO_CURRENCIES, array_indexes=[0],
                                item_title="Subreddit name",
                                title="Subreddits to watch")
        }

    @classmethod
    def get_is_cryptocurrencies_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency dependant else False
        """
        return False

    @classmethod
    def get_is_cryptocurrency_name_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency name dependant else False
        """
        return False

    def _print_entry(self, entry_text, entry_note, count=""):
        self.logger.debug(f"New reddit entry ! : {entry_note} | {count} : {self.cryptocurrency_name} : "
                          f"Link : {entry_text}")

    async def _feed_callback(self, data):
        if self._is_interested_by_this_notification(data[services_constants.FEED_METADATA]):
            self.count += 1
            entry_note = self._get_sentiment(data[CONFIG_REDDIT_ENTRY])
            if entry_note != commons_constants.START_PENDING_EVAL_NOTE:
                self.overall_state_analyser.add_evaluation(entry_note, data[CONFIG_REDDIT_ENTRY_WEIGHT], False)
                if data[CONFIG_REDDIT_ENTRY_WEIGHT] > 3:
                    link = f"https://www.reddit.com{data[CONFIG_REDDIT_ENTRY].permalink}"
                    self._print_entry(link, entry_note, str(self.count))
                self.eval_note = self.overall_state_analyser.get_overall_state_after_refresh()
                await self.evaluation_completed(self.cryptocurrency, eval_time=self.get_current_exchange_time())

    def _get_sentiment(self, entry):
        # analysis entry text and gives overall sentiment
        reddit_entry_min_length = 50
        # ignore usless (very short) entries
        if entry.selftext and len(entry.selftext) >= reddit_entry_min_length:
            return -1 * self.sentiment_analyser.analyse(entry.selftext)
        return commons_constants.START_PENDING_EVAL_NOTE

    def _is_interested_by_this_notification(self, notification_description):
        # true if the given subreddit is in this cryptocurrency's subreddits configuration
        try:
            for subreddit in self.subreddits_by_cryptocurrency[self.cryptocurrency_name]:
                if subreddit.lower() == notification_description:
                    return True
        except KeyError:
            pass
        return False

    def _get_config_elements(self, config_cryptocurrencies, key):
        if config_cryptocurrencies:
            return {
                cc[commons_constants.CONFIG_CRYPTO_CURRENCY]: cc[key]
                for cc in config_cryptocurrencies
                if cc[commons_constants.CONFIG_CRYPTO_CURRENCY] == self.cryptocurrency_name
            }
        return {}

    async def prepare(self):
        self.sentiment_analyser = tentacles_management.get_single_deepest_child_class(EvaluatorUtil.TextAnalysis)()


================================================
FILE: Evaluator/Social/forum_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["RedditForumEvaluator"],
  "tentacles-requirements": ["overall_state_analysis", "text_analysis", "reddit_service_feed"]
}

================================================
FILE: Evaluator/Social/forum_evaluator/resources/RedditForumEvaluator.md
================================================
First initialises using the recent history of the subreddits in RedditForumEvaluator.json then
watches for new posts to update its evaluation. 

Never triggers strategies re-evaluations, acts as a background evaluator


================================================
FILE: Evaluator/Social/news_evaluator/__init__.py
================================================
from .news import TwitterNewsEvaluator

================================================
FILE: Evaluator/Social/news_evaluator/config/TwitterNewsEvaluator.json
================================================
{
    "crypto-currencies": [
        {
            "crypto-currency": "Bitcoin",
            "accounts": [
                "BTCFoundation"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "Ethereum",
            "accounts": [
                "ethereum",
                "VitalikButerin"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "Neo",
            "accounts": [
                "NEO_Blockchain",
                "NEOnewstoday",
                "NEO_council",
                "neotogas",
                "NEO_DevCon",
                "neonexchange",
                "dahongfei"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "ICON",
            "accounts": [
                "helloiconworld"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "NANO",
            "accounts": [
                "nanocurrency"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "VeChain",
            "accounts": [
                "sunshinelu24",
                "VechainThorCom",
                "Vechain1"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "VeChain Thor",
            "accounts": [
                "sunshinelu24",
                "VechainThorCom",
                "Vechain1"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "Substratum",
            "accounts": [
                "SubstratumNet"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "Ethos",
            "accounts": [
                "Ethos_io"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "Ontology",
            "accounts": [
                "OntologyNetwork"
            ],
            "hashtags": []
        },
        {
            "crypto-currency": "Binance Coin",
            "accounts": [],
            "hashtags": []
        }
    ]
}

================================================
FILE: Evaluator/Social/news_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["TwitterNewsEvaluator"],
  "tentacles-requirements": ["text_analysis", "twitter_service_feed"]
}

================================================
FILE: Evaluator/Social/news_evaluator/news.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.

import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums

import octobot_commons.tentacles_management as tentacles_management
import octobot_services.constants as services_constants
import octobot_evaluators.evaluators as evaluators
from tentacles.Evaluator.Util.text_analysis import TextAnalysis
import tentacles.Services.Services_feeds as Services_feeds


# disable inheritance to disable tentacle visibility. Disabled as starting from feb 9 2023, API is now paid only
# class TwitterNewsEvaluator(evaluators.SocialEvaluator):
class TwitterNewsEvaluator:
    SERVICE_FEED_CLASS = Services_feeds.TwitterServiceFeed if hasattr(Services_feeds, 'TwitterServiceFeed') else None

    # max time to live for a pulse is 10min
    _EVAL_MAX_TIME_TO_LIVE = 10 * commons_constants.MINUTE_TO_SECONDS
    # absolute value above which a notification is triggered
    _EVAL_NOTIFICATION_THRESHOLD = 0.6

    def __init__(self, tentacles_setup_config):
        super().__init__(tentacles_setup_config)
        self.count = 0
        self.sentiment_analyser = None
        self.is_self_refreshing = True
        self.accounts_by_cryptocurrency = {}
        self.hashtags_by_cryptocurrency = {}

    def init_user_inputs(self, inputs: dict) -> None:
        """
        Called right before starting the tentacle, should define all the tentacle's user inputs unless
        those are defined somewhere else.
        """
        cryptocurrencies = []
        config_cryptocurrencies = self.UI.user_input(
            commons_constants.CONFIG_CRYPTO_CURRENCIES, commons_enums.UserInputTypes.OBJECT_ARRAY,
            cryptocurrencies, inputs, other_schema_values={"minItems": 1, "uniqueItems": True},
            item_title="Crypto currency",
            title="Crypto currencies to watch."
        )
        # init one user input to generate user input schema and default values
        cryptocurrencies.append(self._init_cryptocurrencies(inputs, "Bitcoin", ["BTCFoundation"], []))
        # remove other symbols data to avoid unnecessary entries
        self.accounts_by_cryptocurrency = self._get_config_elements(config_cryptocurrencies,
                                                                    services_constants.CONFIG_TWITTERS_ACCOUNTS)
        self.hashtags_by_cryptocurrency = self._get_config_elements(config_cryptocurrencies,
                                                                    services_constants.CONFIG_TWITTERS_HASHTAGS)
        self.feed_config[services_constants.CONFIG_TWITTERS_ACCOUNTS] = self.accounts_by_cryptocurrency
        self.feed_config[services_constants.CONFIG_TWITTERS_HASHTAGS] = self.hashtags_by_cryptocurrency

    def _init_cryptocurrencies(self, inputs, cryptocurrency, accounts, hashtags):
        return {
            commons_constants.CONFIG_CRYPTO_CURRENCY:
                self.UI.user_input(commons_constants.CONFIG_CRYPTO_CURRENCY, commons_enums.UserInputTypes.TEXT,
                                cryptocurrency, inputs, other_schema_values={"minLength": 2},
                                parent_input_name=commons_constants.CONFIG_CRYPTO_CURRENCIES, array_indexes=[0],
                                title="Crypto currency name"),
            services_constants.CONFIG_TWITTERS_ACCOUNTS:
                self.UI.user_input(services_constants.CONFIG_TWITTERS_ACCOUNTS, commons_enums.UserInputTypes.STRING_ARRAY,
                                accounts, inputs, other_schema_values={"uniqueItems": True},
                                parent_input_name=commons_constants.CONFIG_CRYPTO_CURRENCIES, array_indexes=[0],
                                item_title="Twitter account name",
                                title="Twitter accounts to watch"),
            services_constants.CONFIG_TWITTERS_HASHTAGS:
                self.UI.user_input(services_constants.CONFIG_TWITTERS_HASHTAGS, commons_enums.UserInputTypes.STRING_ARRAY,
                                hashtags, inputs, other_schema_values={"uniqueItems": True},
                                parent_input_name=commons_constants.CONFIG_CRYPTO_CURRENCIES, array_indexes=[0],
                                item_title="Hashtag",
                                title="Twitter hashtags to watch (without the # character), "
                                      "warning: might trigger evaluator for irrelevant tweets.")
        }

    @classmethod
    def get_is_cryptocurrencies_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency dependant else False
        """
        return False

    @classmethod
    def get_is_cryptocurrency_name_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency name dependant else False
        """
        return False

    def _print_tweet(self, tweet_text, tweet_url, note, count=""):
        self.logger.debug(f"Current note : {note} | {count} : {self.cryptocurrency_name} : Link: {tweet_url} Text : "
                          f"{tweet_text.encode('utf-8', 'ignore')}")

    async def _feed_callback(self, data):
        if self._is_interested_by_this_notification(data[services_constants.CONFIG_TWEET_DESCRIPTION]):
            self.count += 1
            note = self._get_tweet_sentiment(data[services_constants.CONFIG_TWEET],
                                             data[services_constants.CONFIG_TWEET_DESCRIPTION])
            tweet_url = f"https://twitter.com/ProducToken/status/{data['tweet']['id']}"
            if note != commons_constants.START_PENDING_EVAL_NOTE:
                self._print_tweet(data[services_constants.CONFIG_TWEET_DESCRIPTION], tweet_url, note, str(self.count))
            await self._check_eval_note(note)

    # only set eval note when something is happening
    async def _check_eval_note(self, note):
        if note != commons_constants.START_PENDING_EVAL_NOTE:
            if abs(note) > self._EVAL_NOTIFICATION_THRESHOLD:
                self.eval_note = note
                self.save_evaluation_expiration_time(self._compute_notification_time_to_live(self.eval_note))
                await self.evaluation_completed(self.cryptocurrency, eval_time=self.get_current_exchange_time())

    @staticmethod
    def _compute_notification_time_to_live(evaluation):
        return TwitterNewsEvaluator._EVAL_MAX_TIME_TO_LIVE * abs(evaluation)

    def _get_tweet_sentiment(self, tweet, tweet_text, is_a_quote=False):
        try:
            if is_a_quote:
                return -1 * self.sentiment_analyser.analyse(tweet_text)
            else:
                padding_name = "########"
                author_screen_name = tweet['user']['screen_name'] if "screen_name" in tweet['user'] \
                    else padding_name
                author_name = tweet['user']['name'] if "name" in tweet['user'] else padding_name
                if author_screen_name in self.accounts_by_cryptocurrency[self.cryptocurrency_name] \
                        or author_name in self.accounts_by_cryptocurrency[self.cryptocurrency_name]:
                    return -1 * self.sentiment_analyser.analyse(tweet_text)
        except KeyError:
            pass

        # ignore # for the moment (too much of bullshit)
        return commons_constants.START_PENDING_EVAL_NOTE

    def _is_interested_by_this_notification(self, notification_description):
        # true if in twitter accounts
        try:
            for account in self.accounts_by_cryptocurrency[self.cryptocurrency_name]:
                if account.lower() in notification_description:
                    return True
        except KeyError:
            return False
        # false if it's a RT of an unfollowed account
        if notification_description.startswith("rt"):
            return False

        # true if contains symbol
        if self.cryptocurrency_name.lower() in notification_description:
            return True

        # true if in hashtags
        if self.hashtags_by_cryptocurrency:
            for hashtags in self.hashtags_by_cryptocurrency[self.cryptocurrency_name]:
                if hashtags.lower() in notification_description:
                    return True
            return False

    def _get_config_elements(self, config_cryptocurrencies, key):
        if config_cryptocurrencies:
            return {
                cc[commons_constants.CONFIG_CRYPTO_CURRENCY]: cc[key]
                for cc in config_cryptocurrencies
                if cc[commons_constants.CONFIG_CRYPTO_CURRENCY] == self.cryptocurrency_name
            }
        return {}

    async def prepare(self):
        self.sentiment_analyser = tentacles_management.get_single_deepest_child_class(TextAnalysis)()


================================================
FILE: Evaluator/Social/news_evaluator/resources/TwitterNewsEvaluator.md
================================================
Triggers when a new tweet appears from a Twitter account in TwitterNewsEvaluator.json.

If the evaluation of any given tweet is significant enough, triggers strategies re-evaluation. Otherwise 
acts as a background evaluator.

================================================
FILE: Evaluator/Social/signal_evaluator/__init__.py
================================================
from .signal import TelegramSignalEvaluator, TelegramChannelSignalEvaluator

================================================
FILE: Evaluator/Social/signal_evaluator/config/TelegramChannelSignalEvaluator.json
================================================
{
    "telegram-channels": [
        {
            "channel_name": "Test-Channel",
            "signal_pair": "Pair: (.*)$",
            "signal_pattern": {
                "MARKET_BUY": "Side: (BUY)$",
                "MARKET_SELL": "Side: (SELL)$"
            }
        }
    ]
}

================================================
FILE: Evaluator/Social/signal_evaluator/config/TelegramSignalEvaluator.json
================================================
{
    "telegram-channels": [
        "test_telegram_signal_strat"
    ]
}

================================================
FILE: Evaluator/Social/signal_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["TelegramSignalEvaluator", "TelegramChannelSignalEvaluator"],
  "tentacles-requirements": ["telegram_service_feed"]
}

================================================
FILE: Evaluator/Social/signal_evaluator/resources/TelegramChannelSignalEvaluator.md
================================================
Evaluator that catch Telegram channel signals.

Triggers on a Telegram signal from any channel your personal account joined.

Signal parsing is configurable according to the name of the channel.

See [OctoBot docs about Telegram API service](https://www.octobot.cloud/en/guides/octobot-interfaces/telegram/telegram-api?utm_source=octobot&utm_medium=dk&utm_campaign=regular_open_source_content&utm_content=telegramChannelSignalEvaluator) for more information.


================================================
FILE: Evaluator/Social/signal_evaluator/resources/TelegramSignalEvaluator.md
================================================
Very simple evaluator designed to be an example for an evaluator using Telegram signals.

Triggers on a Telegram signal from any group or channel listed in this evaluator configuration in which 
your Telegram bot is invited.

Signal format for this implementation is: **SYMBOL[evaluation]**. Example: **BTC/USDT[-0.45]**.

SYMBOL has to be in current watched symbols (in configuration) and evaluation must be between -1 and 1. 

Remember that OctoBot can only see messages from a
chat/group where its Telegram bot (in OctoBot configuration) has been invited. Keep also in mind that you
need to disable the privacy mode of your Telegram bot to allow it to see group messages.

See [OctoBot docs about Telegram interface](https://www.octobot.cloud/en/guides/octobot-interfaces/telegram?utm_source=octobot&utm_medium=dk&utm_campaign=regular_open_source_content&utm_content=telegramSignalEvaluator) for more information.


================================================
FILE: Evaluator/Social/signal_evaluator/signal.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import re

import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_services.constants as services_constants
import octobot_evaluators.evaluators as evaluators
import tentacles.Services.Services_feeds as Services_feeds


class TelegramSignalEvaluator(evaluators.SocialEvaluator):
    SERVICE_FEED_CLASS = Services_feeds.TelegramServiceFeed if hasattr(Services_feeds, 'TelegramServiceFeed') else None

    def init_user_inputs(self, inputs: dict) -> None:
        channels_config = self.UI.user_input(services_constants.CONFIG_TELEGRAM_CHANNEL,
                                          commons_enums.UserInputTypes.STRING_ARRAY,
                                          [], inputs, item_title="Channel name",
                                          title="Name of the watched channels")
        self.feed_config[services_constants.CONFIG_TELEGRAM_CHANNEL] = channels_config

    async def _feed_callback(self, data):
        if self._is_interested_by_this_notification(data[services_constants.CONFIG_GROUP_MESSAGE_DESCRIPTION]):
            await self.analyse_notification(data)
            await self.evaluation_completed(self.cryptocurrency, self.symbol,
                                            eval_time=self.get_current_exchange_time())
        else:
            self.logger.debug(f"Ignored telegram feed: \"{self.symbol.lower()}\" pattern not found in "
                              f"\"{data[services_constants.CONFIG_GROUP_MESSAGE_DESCRIPTION].lower()}\"")

    # return true if the given notification is relevant for this client
    def _is_interested_by_this_notification(self, notification_description):
        if self.symbol:
            return self.symbol.lower() in notification_description.lower()
        else:
            return True

    async def analyse_notification(self, notification):
        notification_test = notification[services_constants.CONFIG_GROUP_MESSAGE_DESCRIPTION]
        self.eval_note = commons_constants.START_PENDING_EVAL_NOTE
        start_eval_chars = "["
        end_eval_chars = "]"
        if start_eval_chars in notification_test and end_eval_chars in notification_test:
            try:
                split_test = notification_test.split(start_eval_chars)
                notification_eval = split_test[1].split(end_eval_chars)[0]
                potential_note = float(notification_eval)
                if -1 <= potential_note <= 1:
                    self.eval_note = potential_note
                else:
                    self.logger.error(f"Impossible to use notification evaluation: {notification_eval}: "
                                      f"evaluation should be between -1 and 1.")
            except Exception as e:
                self.logger.error(f"Impossible to parse notification {notification_test}: {e}. Please refer to this "
                                  f"evaluator documentation to check the notification pattern.")
        else:
            self.logger.error(f"Impossible to parse notification {notification_test}. Please refer to this evaluator "
                              f"documentation to check the notification pattern.")

    @classmethod
    def get_is_cryptocurrencies_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency dependant else False
        """
        return False

    @classmethod
    def get_is_cryptocurrency_name_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency name dependant else False
        """
        return False

    @classmethod
    def get_is_symbol_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not symbol dependant else False
        """
        return False

    def _get_tentacle_registration_topic(self, all_symbols_by_crypto_currencies, time_frames, real_time_time_frames):
        currencies = [self.cryptocurrency]
        symbols = [self.symbol]
        to_handle_time_frames = [self.time_frame]
        if self.get_is_cryptocurrencies_wildcard():
            currencies = all_symbols_by_crypto_currencies.keys()
        if self.get_is_symbol_wildcard():
            symbols = []
            for currency_symbols in all_symbols_by_crypto_currencies.values():
                symbols += currency_symbols
        # by default no time frame registration for social evaluators
        return currencies, symbols, to_handle_time_frames


class TelegramChannelSignalEvaluator(evaluators.SocialEvaluator):
    SERVICE_FEED_CLASS = Services_feeds.TelegramApiServiceFeed if hasattr(Services_feeds, 'TelegramApiServiceFeed') else None

    SIGNAL_PATTERN_KEY = "signal_pattern"
    SIGNAL_PATTERN_MARKET_BUY_KEY = "MARKET_BUY"
    SIGNAL_PATTERN_MARKET_SELL_KEY = "MARKET_SELL"
    SIGNAL_PAIR_KEY = "signal_pair"
    SIGNAL_CHANNEL_NAME_KEY = "channel_name"

    def __init__(self, tentacles_setup_config):
        super().__init__(tentacles_setup_config)
        self.channels_config_by_channel_name = {}

    def init_user_inputs(self, inputs: dict) -> None:
        channels = []
        config_channels = self.UI.user_input(services_constants.CONFIG_TELEGRAM_CHANNEL,
                                          commons_enums.UserInputTypes.OBJECT_ARRAY,
                                          channels, inputs, item_title="Channel",
                                          other_schema_values={"minItems": 1, "uniqueItems": True},
                                          title="Channels to watch")
        channels.append(self._init_channel_config(inputs, "Test-Channel", "Pair: (.*)$",
                                                  "Side: (BUY)$", "Side: (SELL)$"))
        self.channels_config_by_channel_name = {
            channel[self.SIGNAL_CHANNEL_NAME_KEY]: channel
            for channel in config_channels
        }
        self.feed_config[services_constants.CONFIG_TELEGRAM_CHANNEL] = list(self.channels_config_by_channel_name)

    def _init_channel_config(self, inputs, channel_name, signal_pair, buy_regex, sell_regex):
        return {
            self.SIGNAL_CHANNEL_NAME_KEY: self.UI.user_input(
                self.SIGNAL_CHANNEL_NAME_KEY, commons_enums.UserInputTypes.TEXT,
                channel_name, inputs,
                parent_input_name=services_constants.CONFIG_TELEGRAM_CHANNEL,
                array_indexes=[0],
                title="Channel name"),
            self.SIGNAL_PAIR_KEY: self.UI.user_input(
                self.SIGNAL_PAIR_KEY, commons_enums.UserInputTypes.TEXT,
                signal_pair, inputs,
                parent_input_name=services_constants.CONFIG_TELEGRAM_CHANNEL,
                array_indexes=[0],
                title="Trading pair regex, ex: Pair: (.*)$"),
            self.SIGNAL_PATTERN_KEY: self.UI.user_input(
                self.SIGNAL_PATTERN_KEY, commons_enums.UserInputTypes.OBJECT,
                self._init_pattern_config(inputs, buy_regex, sell_regex), inputs,
                parent_input_name=services_constants.CONFIG_TELEGRAM_CHANNEL,
                array_indexes=[0],
                title="Signal patterns"),
        }

    def _init_pattern_config(self, inputs, buy_regex, sell_regex):
        return {
            self.SIGNAL_PATTERN_MARKET_BUY_KEY: self.UI.user_input(
                self.SIGNAL_PATTERN_MARKET_BUY_KEY, commons_enums.UserInputTypes.TEXT,
                buy_regex, inputs, parent_input_name=self.SIGNAL_PATTERN_KEY,
                array_indexes=[0],
                title="Market buy signal regex, ex: Side: (BUY)$"),
            self.SIGNAL_PATTERN_MARKET_SELL_KEY: self.UI.user_input(
                self.SIGNAL_PATTERN_MARKET_SELL_KEY,
                commons_enums.UserInputTypes.TEXT,
                sell_regex, inputs,
                parent_input_name=self.SIGNAL_PATTERN_KEY,
                array_indexes=[0],
                title="Market sell signal regex, ex: Side: (SELL)$"),
        }

    async def _feed_callback(self, data):
        if not data:
            return
        is_from_channel = data.get(services_constants.CONFIG_IS_CHANNEL_MESSAGE, False)
        if is_from_channel:
            sender = data.get(services_constants.CONFIG_MESSAGE_SENDER, "")
            if sender in self.channels_config_by_channel_name:
                try:
                    message = data.get(services_constants.CONFIG_MESSAGE_CONTENT, "")
                    channel_data = self.channels_config_by_channel_name[sender]
                    is_buy_market_signal = self._get_signal_message(
                        channel_data[self.SIGNAL_PATTERN_KEY][self.SIGNAL_PATTERN_MARKET_BUY_KEY], message)
                    is_sell_market_signal = self._get_signal_message(
                        channel_data[self.SIGNAL_PATTERN_KEY][self.SIGNAL_PATTERN_MARKET_SELL_KEY], message)
                    pair = self._get_signal_message(channel_data[self.SIGNAL_PAIR_KEY], message)
                    if (is_buy_market_signal or is_sell_market_signal) and pair is not None:
                        self.eval_note = -1 if is_buy_market_signal else 1
                        await self.evaluation_completed(symbol=pair.strip(), eval_time=self.get_current_exchange_time())
                    else:
                        self.logger.warning(f"Unable to parse message from {sender} : {message}")
                except KeyError:
                    self.logger.warning(f"Unable to parse message from {sender}")
            else:
                self.logger.debug(f"Ignored message : from an unsupported channel ({sender})")
        else:
            self.logger.debug("Ignored message : not a channel message")

    def _get_signal_message(self, expected_pattern, message):
        try:
            match = re.search(expected_pattern, message)
            return match.group(1)
        except AttributeError:
            self.logger.debug(f"Ignored message : not matching channel pattern ({message})")
        return None


================================================
FILE: Evaluator/Social/signal_evaluator/tests/__init__.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.


================================================
FILE: Evaluator/Social/signal_evaluator/tests/test_telegram_channel_signal_evaluator.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.

import pytest
import octobot_commons.constants as commons_constants
import octobot_commons.logging as logging
import octobot_services.constants as services_constants
import tentacles.Evaluator.Social as Social
import tests.test_utils.config as test_utils_config

# All test coroutines will be treated as marked.
pytestmark = pytest.mark.asyncio


async def _trigger_callback_with_data_and_assert_note(evaluator: Social.TelegramChannelSignalEvaluator,
                                                      data=None,
                                                      note=commons_constants.START_PENDING_EVAL_NOTE):
    await evaluator._feed_callback(data)
    assert evaluator.eval_note == note
    evaluator.eval_note = commons_constants.START_PENDING_EVAL_NOTE


def _create_evaluator_with_supported_channel_signals():
    evaluator = Social.TelegramChannelSignalEvaluator(test_utils_config.load_test_tentacles_config())
    evaluator.logger = logging.get_logger(evaluator.get_name())
    evaluator.specific_config = {
        "telegram-channels": [
            {
                "channel_name": "TEST-CHAN-1",
                "signal_pattern": {
                    "MARKET_BUY": "Side: (BUY)",
                    "MARKET_SELL": "Side: (SELL)"
                },
                "signal_pair": "Pair: (.*)"
            },
            {
                "channel_name": "TEST-CHAN-2",
                "signal_pattern": {
                    "MARKET_BUY": ".* : (-1)$",
                    "MARKET_SELL": ".* : (1)$"
                },
                "signal_pair": "(.*):"
            }
        ]
    }
    evaluator.init_user_inputs({})
    evaluator.eval_note = commons_constants.START_PENDING_EVAL_NOTE
    return evaluator


async def test_without_data():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator)


async def test_with_empty_data():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={})


async def test_incorrect_signal_without_sender_without_channel_message():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: False,
        services_constants.CONFIG_MESSAGE_SENDER: "",
        services_constants.CONFIG_MESSAGE_CONTENT: "",
    })


async def test_incorrect_signal_without_sender_with_channel_message():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "",
        services_constants.CONFIG_MESSAGE_CONTENT: "",
    })


async def test_incorrect_signal_chan1_without_content():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-1",
        services_constants.CONFIG_MESSAGE_CONTENT: "",
    })


async def test_incorrect_signal_chan1_without_coin():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-1",
        services_constants.CONFIG_MESSAGE_CONTENT: """
        Order Id: 1631033831358699
        Pair: 
        Side:
        Price: 12.909
        """,
    })


async def test_incorrect_signal_chan1_without_separator():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-1",
        services_constants.CONFIG_MESSAGE_CONTENT: """
        Order Id: 1631033831358699
        Pair QTUMUSDT
        Side: BUY
        Price: 12.909
        """,
    })


async def test_correct_signal_chan1_with_not_channel_message():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: False,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-1",
        services_constants.CONFIG_MESSAGE_CONTENT: """
        Order Id: 1631033831358699
        Pair: QTUMUSDT
        Side: BUY
        Price: 12.909
        """,
    })


async def test_correct_signal_chan1_with_chan2():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-2",
        services_constants.CONFIG_MESSAGE_CONTENT: """
        Order Id: 1631033831358699
        Pair: QTUMUSDT
        Side: BUY
        Price: 12.909
        """,
    })


async def test_correct_signal_chan1():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-1",
        services_constants.CONFIG_MESSAGE_CONTENT: """
        Order Id: 1631033831358699
        Pair: QTUMUSDT
        Side: BUY
        Price: 12.909
        """,
    }, note=-1)


async def test_correct_signal_chan2_but_with_chan1():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-1",
        services_constants.CONFIG_MESSAGE_CONTENT: "BTC/USDT : 1",
    })


async def test_correct_signal_chan2():
    evaluator = _create_evaluator_with_supported_channel_signals()
    await _trigger_callback_with_data_and_assert_note(evaluator, data={
        services_constants.CONFIG_IS_CHANNEL_MESSAGE: True,
        services_constants.CONFIG_MESSAGE_SENDER: "TEST-CHAN-2",
        services_constants.CONFIG_MESSAGE_CONTENT: "BTC/USDT : -1",
    }, note=-1)


================================================
FILE: Evaluator/Social/trends_evaluator/__init__.py
================================================
from .trends import GoogleTrendsEvaluator

================================================
FILE: Evaluator/Social/trends_evaluator/config/GoogleTrendsEvaluator.json
================================================
{
  "refresh_rate_seconds" : 86400,
  "relevant_history_months" : 3
}


================================================
FILE: Evaluator/Social/trends_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["GoogleTrendsEvaluator"],
  "tentacles-requirements": ["statistics_analysis", "google_service_feed"]
}

================================================
FILE: Evaluator/Social/trends_evaluator/resources/GoogleTrendsEvaluator.md
================================================
Analyses the popularity of the given currencies using their names. 

Data are provided by [Google's trends service](https://trends.google.com/trends/?geo=US).
 
Due to Google trends poor refresh rate, this evaluation should be considered for large time frames only.

================================================
FILE: Evaluator/Social/trends_evaluator/trends.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import numpy

import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.tentacles_management as tentacles_management
import octobot_evaluators.evaluators as evaluators
import octobot_services.constants as services_constants
import tentacles.Evaluator.Util as EvaluatorUtil
import tentacles.Services.Services_feeds as Services_feeds


class GoogleTrendsEvaluator(evaluators.SocialEvaluator):
    SERVICE_FEED_CLASS = Services_feeds.GoogleServiceFeed if hasattr(Services_feeds, 'GoogleServiceFeed') else None

    def __init__(self, tentacles_setup_config):
        evaluators.SocialEvaluator.__init__(self, tentacles_setup_config)
        self.stats_analyser = None
        self.refresh_rate_seconds = 86400
        self.relevant_history_months = 3

    def init_user_inputs(self, inputs: dict) -> None:
        self.refresh_rate_seconds = self.refresh_rate_seconds or \
            self.UI.user_input(commons_constants.CONFIG_REFRESH_RATE,
                               commons_enums.UserInputTypes.INT,
                               self.refresh_rate_seconds, inputs, min_val=1,
                               title="Seconds between each re-evaluation (do not set too low because google has a low "
                                     "monthly rate limit).")
        self.relevant_history_months = self.UI.user_input(services_constants.CONFIG_TREND_HISTORY_TIME,
                                                       commons_enums.UserInputTypes.INT,
                                                       self.relevant_history_months, inputs, min_val=3, max_val=3,
                                                       title="Number of months to look into to compute the trend "
                                                             "evaluation (for now works only with 3).")
        self.feed_config[services_constants.CONFIG_TREND_TOPICS] = self._build_trend_topics()

    @classmethod
    def get_is_cryptocurrencies_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency dependant else False
        """
        return False

    @classmethod
    def get_is_cryptocurrency_name_wildcard(cls) -> bool:
        """
        :return: True if the evaluator is not cryptocurrency name dependant else False
        """
        return False

    async def _feed_callback(self, data):
        if self._is_interested_by_this_notification(data[services_constants.FEED_METADATA]):
            trend = numpy.array([d["data"] for d in data[services_constants.CONFIG_TREND]])
            # compute bollinger bands
            self.eval_note = self.stats_analyser.analyse_recent_trend_changes(trend, numpy.sqrt)
            await self.evaluation_completed(self.cryptocurrency, eval_time=self.get_current_exchange_time())

    def _is_interested_by_this_notification(self, notification_description):
        return self.cryptocurrency_name in notification_description

    def _build_trend_topics(self):
        trend_time_frame = f"today {self.relevant_history_months}-m"
        return [
            Services_feeds.TrendTopic(self.refresh_rate_seconds,
                                      [self.cryptocurrency_name],
                                      time_frame=trend_time_frame)
        ]

    async def prepare(self):
        self.stats_analyser = tentacles_management.get_single_deepest_child_class(EvaluatorUtil.StatisticAnalysis)()


================================================
FILE: Evaluator/Strategies/blank_strategy_evaluator/__init__.py
================================================
from .blank_strategy import BlankStrategyEvaluator


================================================
FILE: Evaluator/Strategies/blank_strategy_evaluator/blank_strategy.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import octobot_commons.constants as common_constants
import octobot_commons.enums as common_enums
import octobot_evaluators.evaluators as evaluators
import octobot_evaluators.enums as enums


class BlankStrategyEvaluator(evaluators.StrategyEvaluator):

    def init_user_inputs(self, inputs: dict) -> None:
        """
        Called right before starting the tentacle, should define all the tentacle's user inputs unless
        those are defined somewhere else.
        """
        super().init_user_inputs(inputs)
        self.UI.user_input(common_constants.CONFIG_TENTACLES_REQUIRED_CANDLES_COUNT, common_enums.UserInputTypes.INT,
                        200, inputs, min_val=1,
                        title="Initialization candles count: the number of historical candles to fetch from "
                              "exchanges when OctoBot is starting.")

    def get_full_cycle_evaluator_types(self) -> tuple:
        # returns a tuple as it is faster to create than a list
        return enums.EvaluatorMatrixTypes.TA.value, enums.EvaluatorMatrixTypes.SCRIPTED.value

    async def matrix_callback(self,
                              matrix_id,
                              evaluator_name,
                              evaluator_type,
                              eval_note,
                              eval_note_type,
                              exchange_name,
                              cryptocurrency,
                              symbol,
                              time_frame):
        self.eval_note = eval_note
        await self.strategy_completed(cryptocurrency, symbol, time_frame=time_frame)


================================================
FILE: Evaluator/Strategies/blank_strategy_evaluator/config/BlankStrategyEvaluator.json
================================================
{
  "required_time_frames" : ["1h"],
  "required_evaluators" : ["*"],
  "required_candles_count" : 200,
  "default_config" : ["ScriptedEvaluator"]
}

================================================
FILE: Evaluator/Strategies/blank_strategy_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["BlankStrategyEvaluator"],
  "tentacles-requirements": []
}

================================================
FILE: Evaluator/Strategies/blank_strategy_evaluator/resources/BlankStrategyEvaluator.md
================================================
BlankStrategyEvaluator is forwarding evaluator values to the trading mode.


================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/__init__.py
================================================
from .dip_analyser_strategy import DipAnalyserStrategyEvaluator

================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/config/DipAnalyserStrategyEvaluator.json
================================================
{
    "default_config": [
        "KlingerOscillatorReversalConfirmationMomentumEvaluator",
        "RSIWeightMomentumEvaluator"
    ],
    "required_evaluators": [
        "InstantFluctuationsEvaluator",
        "KlingerOscillatorReversalConfirmationMomentumEvaluator",
        "RSIWeightMomentumEvaluator"
    ],
    "required_time_frames": [
        "4h"
    ]
}

================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/dip_analyser_strategy.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import typing

import octobot_commons.enums as commons_enums
import octobot_commons.constants as commons_constants
import octobot_evaluators.api.matrix as evaluators_api
import octobot_evaluators.evaluators.channel as evaluator_channel
import octobot_evaluators.constants as evaluator_constants
import octobot_evaluators.matrix as matrix
import octobot_evaluators.enums as evaluators_enums
import octobot_evaluators.evaluators as evaluators
import octobot_tentacles_manager.api as tentacles_manager_api
import octobot_trading.api as trading_api
import tentacles.Evaluator.TA as TA


class DipAnalyserStrategyEvaluator(evaluators.StrategyEvaluator):
    REVERSAL_CONFIRMATION_CLASS_NAME = TA.KlingerOscillatorReversalConfirmationMomentumEvaluator.get_name()
    REVERSAL_WEIGHT_CLASS_NAME = TA.RSIWeightMomentumEvaluator.get_name()

    @staticmethod
    def get_eval_type():
        return typing.Dict[str, int]

    def __init__(self, tentacles_setup_config):
        super().__init__(tentacles_setup_config)
        self.evaluation_time_frame = None

    def init_user_inputs(self, inputs: dict) -> None:
        """
        Called right before starting the tentacle, should define all the tentacle's user inputs unless
        those are defined somewhere else.
        """
        self.evaluation_time_frame = self.evaluation_time_frame or commons_enums.TimeFrames(
            self.UI.user_input(
                evaluator_constants.STRATEGIES_REQUIRED_TIME_FRAME,
                commons_enums.UserInputTypes.MULTIPLE_OPTIONS,
                [commons_enums.TimeFrames.ONE_HOUR.value],
                inputs, options=[tf.value for tf in commons_enums.TimeFrames],
                title="Analysed time frame: only the first one will be considered for DipAnalyserStrategyEvaluator."
            )[0]
        ).value

    async def matrix_callback(self,
                              matrix_id,
                              evaluator_name,
                              evaluator_type,
                              eval_note,
                              eval_note_type,
                              exchange_name,
                              cryptocurrency,
                              symbol,
                              time_frame):
        if evaluator_type == evaluators_enums.EvaluatorMatrixTypes.REAL_TIME.value:
            # trigger re-evaluation
            exchange_id = trading_api.get_exchange_id_from_matrix_id(exchange_name, matrix_id)
            await evaluator_channel.trigger_technical_evaluators_re_evaluation_with_updated_data(matrix_id,
                                                                                                 evaluator_name,
                                                                                                 evaluator_type,
                                                                                                 exchange_name,
                                                                                                 cryptocurrency,
                                                                                                 symbol,
                                                                                                 exchange_id,
                                                                                                 self.strategy_time_frames)
            # do not continue this evaluation
            return
        elif evaluator_type == evaluators_enums.EvaluatorMatrixTypes.TA.value:
            self.eval_note = commons_constants.START_PENDING_EVAL_NOTE
            TA_evaluations = matrix.get_evaluations_by_evaluator(matrix_id,
                                                                 exchange_name,
                                                                 evaluators_enums.EvaluatorMatrixTypes.TA.value,
                                                                 cryptocurrency,
                                                                 symbol,
                                                                 self.evaluation_time_frame,
                                                                 allowed_values=[
                                                                     commons_constants.START_PENDING_EVAL_NOTE])

            try:
                if evaluators_api.get_value(TA_evaluations[self.REVERSAL_CONFIRMATION_CLASS_NAME]):
                    self.eval_note = evaluators_api.get_value(TA_evaluations[self.REVERSAL_WEIGHT_CLASS_NAME])
                await self.strategy_completed(cryptocurrency, symbol)
            except KeyError as e:
                self.logger.error(f"Missing required evaluator: {e}")


================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["DipAnalyserStrategyEvaluator"],
  "tentacles-requirements": ["momentum_evaluator.py"]
}

================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/resources/DipAnalyserStrategyEvaluator.md
================================================
DipAnalyserStrategyEvaluator is a strategy analysing market dips using [RSI](https://www.investopedia.com/terms/r/rsi.asp) 
averages. According to the level of the RSI, a buy signal can be generated. This signal has a weight that corresponds to 
a higher or lower intensity of the RSI evaluation.
 
This strategy also uses the [Klinger oscillator](https://www.investopedia.com/terms/k/klingeroscillator.asp) to identify 
reversals and create buy signals. 

A buy signal is generated when the RSI component is signaling an opportunity and the Klinger part is confirming 
a reversal situation.

This strategy is updated at the end of each candle on the watched time frame. 

It is also possible to make it trigger 
automatically using a real-time evaluator. Using a real time evaluator that signals sudden market changes like the 
InstantFluctuationsEvaluator will make DipAnalyserStrategyEvaluator also wake up on such events.

DipAnalyserStrategyEvaluator focuses on one time frame only and works best on larger time frames such as 4h and more.

================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/tests/__init__.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.


================================================
FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/tests/test_dip_analyser_strategy_evaluator.py
================================================
#  Drakkar-Software OctoBot
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import pytest
import decimal

import tests.functional_tests.strategy_evaluators_tests.abstract_strategy_test as abstract_strategy_test
import tentacles.Evaluator.Strategies as Strategies
import tentacles.Trading.Mode as Mode

# All test coroutines will be treated as marked.
pytestmark = pytest.mark.asyncio


@pytest.fixture
def strategy_tester():
    strategy_tester_instance = DipAnalyserStrategiesEvaluatorTest()
    strategy_tester_instance.initialize(Strategies.DipAnalyserStrategyEvaluator, Mode.DipAnalyserTradingMode)
    return strategy_tester_instance


class DipAnalyserStrategiesEvaluatorTest(abstract_strategy_test.AbstractStrategyTest):
    """
    About using this test framework:
    To be called by pytest, tests have to be called manually since the cythonized version of AbstractStrategyTest
    creates an __init__() which prevents the default pytest tests collect process
    """

    # Careful with results here, unlike other strategy tests, this one uses only the 4h timeframe, therefore results
    # are not comparable with regular 1h timeframes strategy tests

    # Cannot use bittrex data since they are not providing 4h timeframe data

    # test_full_mixed_strategies_evaluator.py with only 4h timeframe results are provided for comparison:
    # format: results: (bot profitability, market average profitability)

    async def test_default_run(self):
        # market: -49.25407390406244
        await self.run_test_default_run(decimal.Decimal(str(-24.612)))

    async def test_slow_downtrend(self):
        # market: -49.25407390406244
        # market: -47.50593824228029
        await self.run_test_slow_downtrend(decimal.Decimal(str(-24.612)), decimal.Decimal(str(-33.601)), None, None, skip_extended=True)

    async def test_sharp_downtrend(self):
        # market: -34.67997135795625
        await self.run_test_sharp_downtrend(decimal.Decimal(str(-21.634)), None, skip_extended=True)

    async def test_flat_markets(self):
        # market: -38.07647740440325
        # market: -53.87077652637819
        await self.run_test_flat_markets(decimal.Decimal(str(-20.577)), decimal.Decimal(str(-32.756)), None, None, skip_extended=True)

    async def test_slow_uptrend(self):
        # market: 11.32644122514472
        # market: -36.64596273291926
        await self.run_test_slow_uptrend(decimal.Decimal(str(11.326)), decimal.Decimal(str(-14.248)))

    async def test_sharp_uptrend(self):
        # market: -17.047906776003458
        # market: -18.25837965302341
        await self.run_test_sharp_uptrend(decimal.Decimal(str(3.607)), decimal.Decimal(str(10.956)))

    async def test_up_then_down(self):
        await self.run_test_up_then_down(None, skip_extended=True)


async def test_default_run(strategy_tester):
    await strategy_tester.test_default_run()


async def test_slow_downtrend(strategy_tester):
    await strategy_tester.test_slow_downtrend()


async def test_sharp_downtrend(strategy_tester):
    await strategy_tester.test_sharp_downtrend()


async def test_flat_markets(strategy_tester):
    await strategy_tester.test_flat_markets()


async def test_slow_uptrend(strategy_tester):
    await strategy_tester.test_slow_uptrend()


async def test_sharp_uptrend(strategy_tester):
    await strategy_tester.test_sharp_uptrend()


async def test_up_then_down(strategy_tester):
    await strategy_tester.test_up_then_down()


================================================
FILE: Evaluator/Strategies/mixed_strategies_evaluator/__init__.py
================================================
from .mixed_strategies import SimpleStrategyEvaluator, TechnicalAnalysisStrategyEvaluator

================================================
FILE: Evaluator/Strategies/mixed_strategies_evaluator/config/SimpleStrategyEvaluator.json
================================================
{
    "default_config": [
        "DoubleMovingAverageTrendEvaluator",
        "RSIMomentumEvaluator"
    ],
    "required_evaluators": [
        "*"
    ],
    "required_time_frames": [
        "1h",
        "4h",
        "1d"
    ],
    "required_candles_count": 1000,
    "social_evaluators_notification_timeout": 3600,
    "re_evaluate_TA_when_social_or_realtime_notification": true,
    "background_social_evaluators": [
      "RedditForumEvaluator"
    ]
}

================================================
FILE: Evaluator/Strategies/mixed_strategies_evaluator/config/TechnicalAnalysisStrategyEvaluator.json
================================================
{
    "compatible_evaluator_types": [
        "TA",
        "REAL_TIME"
    ],
    "default_config": [
        "DoubleMovingAverageTrendEvaluator",
        "RSIMomentumEvaluator"
    ],
    "required_evaluators": [
        "*"
    ],
    "required_time_frames": [
        "30m", "1h", "2h", "4h", "1d"
    ],
    "time_frames_to_weight": [
        {
            "time_frame": "30m",
            "weight": 30
        },
        {
            "time_frame": "1h",
            "weight": 50
        },
        {
            "time_frame": "2h",
            "weight": 50
        },
        {
            "time_frame": "4h",
            "weight": 50
        },
        {
            "time_frame": "1d",
            "weight": 30
        }
    ]
}

================================================
FILE: Evaluator/Strategies/mixed_strategies_evaluator/metadata.json
================================================
{
  "version": "1.2.0",
  "origin_package": "OctoBot-Default-Tentacles",
  "tentacles": ["SimpleStrategyEvaluator", "TechnicalAnalysisStrategyEvaluator"],
  "tentacles-requirements": []
}

================================================
FILE: Evaluator/Strategies/mixed_strategies_evaluator/mixed_strategies.py
================================================
#  Drakkar-Software OctoBot-Tentacles
#  Copyright (c) Drakkar-Software, All rights reserved.
#
#  This library is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 3.0 of the License, or (at your option) any later version.
#
#  This library 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
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library.
import typing

import octobot_commons.constants as commons_constants
import octobot_commons.enums as commons_enums
import octobot_commons.evaluators_util as evaluators_util
import octobot_commons.time_frame_manager as time_frame_manager
import octobot_evaluators.api as evaluators_api
import octobot_evaluators.evaluators.channel as evaluators_channel
import octobot_evaluators.matrix as matrix
impor
Download .txt
gitextract_fehgh_us/

├── .coveragerc
├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── Automation/
│   ├── actions/
│   │   ├── cancel_open_order_action/
│   │   │   ├── __init__.py
│   │   │   ├── cancel_open_orders.py
│   │   │   └── metadata.json
│   │   ├── sell_all_currencies_action/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── sell_all_currencies.py
│   │   ├── send_notification_action/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── send_notification.py
│   │   └── stop_trading_action/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── stop_trading.py
│   ├── conditions/
│   │   ├── no_condition_condition/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── no_condition.py
│   │   └── scripted_condition/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── scripted_condition.py
│   └── trigger_events/
│       ├── period_check_event/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── period_check.py
│       ├── price_threshold_event/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── price_threshold.py
│       └── profitability_threshold_event/
│           ├── __init__.py
│           ├── metadata.json
│           └── profitability_threshold.py
├── Backtesting/
│   ├── collectors/
│   │   └── exchanges/
│   │       ├── exchange_bot_snapshot_data_collector/
│   │       │   ├── __init__.py
│   │       │   ├── bot_snapshot_with_history_collector.py
│   │       │   └── metadata.json
│   │       ├── exchange_history_collector/
│   │       │   ├── __init__.py
│   │       │   ├── history_collector.pxd
│   │       │   ├── history_collector.py
│   │       │   ├── metadata.json
│   │       │   └── tests/
│   │       │       ├── __init__.py
│   │       │       └── test_history_collector.py
│   │       └── exchange_live_collector/
│   │           ├── __init__.py
│   │           ├── live_collector.pxd
│   │           ├── live_collector.py
│   │           └── metadata.json
│   ├── converters/
│   │   └── exchanges/
│   │       └── legacy_data_converter/
│   │           ├── __init__.py
│   │           ├── legacy_converter.pxd
│   │           ├── legacy_converter.py
│   │           └── metadata.json
│   └── importers/
│       └── exchanges/
│           └── generic_exchange_importer/
│               ├── __init__.py
│               ├── generic_exchange_importer.pxd
│               ├── generic_exchange_importer.py
│               └── metadata.json
├── Evaluator/
│   ├── RealTime/
│   │   └── instant_fluctuations_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   ├── InstantFluctuationsEvaluator.json
│   │       │   └── InstantMAEvaluator.json
│   │       ├── instant_fluctuations.py
│   │       ├── metadata.json
│   │       └── resources/
│   │           ├── InstantFluctuationsEvaluator.md
│   │           └── InstantMAEvaluator.md
│   ├── Social/
│   │   ├── forum_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── RedditForumEvaluator.json
│   │   │   ├── forum.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── RedditForumEvaluator.md
│   │   ├── news_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── TwitterNewsEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── news.py
│   │   │   └── resources/
│   │   │       └── TwitterNewsEvaluator.md
│   │   ├── signal_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── TelegramChannelSignalEvaluator.json
│   │   │   │   └── TelegramSignalEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   ├── TelegramChannelSignalEvaluator.md
│   │   │   │   └── TelegramSignalEvaluator.md
│   │   │   ├── signal.py
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_telegram_channel_signal_evaluator.py
│   │   └── trends_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   └── GoogleTrendsEvaluator.json
│   │       ├── metadata.json
│   │       ├── resources/
│   │       │   └── GoogleTrendsEvaluator.md
│   │       └── trends.py
│   ├── Strategies/
│   │   ├── blank_strategy_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── blank_strategy.py
│   │   │   ├── config/
│   │   │   │   └── BlankStrategyEvaluator.json
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── BlankStrategyEvaluator.md
│   │   ├── dip_analyser_strategy_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── DipAnalyserStrategyEvaluator.json
│   │   │   ├── dip_analyser_strategy.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── DipAnalyserStrategyEvaluator.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_dip_analyser_strategy_evaluator.py
│   │   ├── mixed_strategies_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── SimpleStrategyEvaluator.json
│   │   │   │   └── TechnicalAnalysisStrategyEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── mixed_strategies.py
│   │   │   ├── resources/
│   │   │   │   ├── SimpleStrategyEvaluator.md
│   │   │   │   └── TechnicalAnalysisStrategyEvaluator.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       ├── test_simple_strategy_evaluator.py
│   │   │       └── test_technical_analysis_strategy_evaluator.py
│   │   └── move_signals_strategy_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   └── MoveSignalsStrategyEvaluator.json
│   │       ├── metadata.json
│   │       ├── move_signals_strategy.py
│   │       ├── resources/
│   │       │   └── MoveSignalsStrategyEvaluator.md
│   │       └── tests/
│   │           ├── __init__.py
│   │           └── test_move_signals_strategy_evaluator.py
│   ├── TA/
│   │   ├── ai_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── ai.py
│   │   │   ├── config/
│   │   │   │   └── GPTEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── GPTEvaluator.md
│   │   │   └── tests/
│   │   │       └── test_ai.py
│   │   ├── momentum_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── ADXMomentumEvaluator.json
│   │   │   │   ├── BBMomentumEvaluator.json
│   │   │   │   ├── EMAMomentumEvaluator.json
│   │   │   │   ├── KlingerOscillatorMomentumEvaluator.json
│   │   │   │   ├── KlingerOscillatorReversalConfirmationMomentumEvaluator.json
│   │   │   │   ├── MACDMomentumEvaluator.json
│   │   │   │   ├── RSIMomentumEvaluator.json
│   │   │   │   └── RSIWeightMomentumEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── momentum.py
│   │   │   ├── resources/
│   │   │   │   ├── ADXMomentumEvaluator.md
│   │   │   │   ├── BBMomentumEvaluator.md
│   │   │   │   ├── EMAMomentumEvaluator.md
│   │   │   │   ├── KlingerOscillatorMomentumEvaluator.md
│   │   │   │   ├── KlingerOscillatorReversalConfirmationMomentumEvaluator.md
│   │   │   │   ├── MACDMomentumEvaluator.md
│   │   │   │   ├── RSIMomentumEvaluator.md
│   │   │   │   └── RSIWeightMomentumEvaluator.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       ├── test_adx_momentum_evaluator.py
│   │   │       ├── test_bollinger_bands_momentum_TA_evaluator.py
│   │   │       ├── test_klinger_TA_evaluator.py
│   │   │       ├── test_macd_TA_evaluator.py
│   │   │       └── test_rsi_TA_evaluator.py
│   │   ├── trend_evaluator/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   ├── DeathAndGoldenCrossEvaluator.json
│   │   │   │   ├── DoubleMovingAverageTrendEvaluator.json
│   │   │   │   ├── EMADivergenceTrendEvaluator.json
│   │   │   │   └── SuperTrendEvaluator.json
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   ├── DeathAndGoldenCrossEvaluator.md
│   │   │   │   ├── DoubleMovingAverageTrendEvaluator.md
│   │   │   │   ├── EMADivergenceTrendEvaluator.md
│   │   │   │   └── SuperTrendEvaluator.md
│   │   │   ├── tests/
│   │   │   │   ├── __init__.py
│   │   │   │   └── test_double_moving_averages_TA_evaluator.py
│   │   │   └── trend.py
│   │   └── volatility_evaluator/
│   │       ├── __init__.py
│   │       ├── config/
│   │       │   └── StochasticRSIVolatilityEvaluator.json
│   │       ├── metadata.json
│   │       ├── resources/
│   │       │   └── StochasticRSIVolatilityEvaluator.md
│   │       └── volatility.py
│   └── Util/
│       ├── candles_util/
│       │   ├── __init__.py
│       │   ├── candles_util.pxd
│       │   ├── candles_util.py
│       │   ├── metadata.json
│       │   └── tests/
│       │       └── test_candles_util.py
│       ├── overall_state_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── overall_state_analysis.py
│       ├── pattern_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── pattern_analysis.py
│       ├── statistics_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── statistics_analysis.py
│       ├── text_analysis/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── text_analysis.py
│       └── trend_analysis/
│           ├── __init__.py
│           ├── metadata.json
│           └── trend_analysis.py
├── LICENSE
├── Meta/
│   ├── DSL_operators/
│   │   ├── exchange_operators/
│   │   │   ├── __init__.py
│   │   │   ├── exchange_operator.py
│   │   │   ├── exchange_private_data_operators/
│   │   │   │   ├── __init__.py
│   │   │   │   └── portfolio_operators.py
│   │   │   ├── exchange_public_data_operators/
│   │   │   │   ├── __init__.py
│   │   │   │   └── ohlcv_operators.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       ├── exchange_public_data_operators/
│   │   │       │   └── test_ohlcv_operators.py
│   │   │       └── test_mocks.py
│   │   ├── python_std_operators/
│   │   │   ├── __init__.py
│   │   │   ├── base_binary_operators.py
│   │   │   ├── base_call_operators.py
│   │   │   ├── base_compare_operators.py
│   │   │   ├── base_expression_operators.py
│   │   │   ├── base_iterable_operators.py
│   │   │   ├── base_name_operators.py
│   │   │   ├── base_nary_operators.py
│   │   │   ├── base_subscripting_operators.py
│   │   │   ├── base_unary_operators.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── test_base_operators.py
│   │   │       └── test_dictionnaries.py
│   │   └── ta_operators/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       ├── ta_operator.py
│   │       ├── tests/
│   │       │   ├── test_docs_examples.py
│   │       │   └── test_tulipy_technical_analysis_operators.py
│   │       └── tulipy_technical_analysis_operators.py
│   └── Keywords/
│       └── scripting_library/
│           ├── TA/
│           │   ├── __init__.py
│           │   └── trigger/
│           │       ├── __init__.py
│           │       └── eval_triggered.py
│           ├── UI/
│           │   ├── __init__.py
│           │   ├── inputs/
│           │   │   ├── __init__.py
│           │   │   ├── library_user_inputs.py
│           │   │   ├── select_candle.py
│           │   │   ├── select_history.py
│           │   │   ├── select_time_frame.py
│           │   │   └── triggers.py
│           │   └── plots/
│           │       ├── __init__.py
│           │       └── displayed_elements.py
│           ├── __init__.py
│           ├── alerts/
│           │   ├── __init__.py
│           │   └── notifications.py
│           ├── backtesting/
│           │   ├── __init__.py
│           │   ├── backtesting_data_collector.py
│           │   ├── backtesting_data_selector.py
│           │   ├── backtesting_intialization.py
│           │   ├── backtesting_settings.py
│           │   ├── default_backtesting_run_analysis_script.py
│           │   ├── metadata.py
│           │   └── run_data_analysis.py
│           ├── configuration/
│           │   ├── __init__.py
│           │   ├── exchanges_configuration.py
│           │   ├── indexes_configuration.py
│           │   ├── profile_data_configuration.py
│           │   └── tentacles_configuration.py
│           ├── constants.py
│           ├── data/
│           │   ├── __init__.py
│           │   ├── reading/
│           │   │   ├── __init__.py
│           │   │   ├── exchange_private_data/
│           │   │   │   ├── __init__.py
│           │   │   │   └── open_positions.py
│           │   │   ├── exchange_public_data.py
│           │   │   ├── metadata_reader.py
│           │   │   └── trading_settings.py
│           │   └── writing/
│           │       ├── __init__.py
│           │       ├── plotting.py
│           │       └── portfolio.py
│           ├── errors.py
│           ├── exchanges/
│           │   ├── __init__.py
│           │   └── local_exchange.py
│           ├── metadata.json
│           ├── orders/
│           │   ├── __init__.py
│           │   ├── cancelling.py
│           │   ├── chaining.py
│           │   ├── editing.py
│           │   ├── grouping.py
│           │   ├── mocks.py
│           │   ├── open_orders.py
│           │   ├── order_tags.py
│           │   ├── order_types/
│           │   │   ├── __init__.py
│           │   │   ├── create_order.py
│           │   │   ├── limit_order.py
│           │   │   ├── market_order.py
│           │   │   ├── scaled_order.py
│           │   │   ├── stop_loss_order.py
│           │   │   ├── trailing_limit_order.py
│           │   │   ├── trailing_market_order.py
│           │   │   └── trailing_stop_loss_order.py
│           │   ├── position_size/
│           │   │   ├── __init__.py
│           │   │   ├── amount.py
│           │   │   └── target_position.py
│           │   └── waiting.py
│           ├── settings/
│           │   ├── __init__.py
│           │   └── script_settings.py
│           └── tests/
│               ├── __init__.py
│               ├── backtesting/
│               │   ├── __init__.py
│               │   ├── data_store.py
│               │   ├── test_backtesting_data_collector.py
│               │   ├── test_collect_data_and_run_backtesting.py
│               │   └── test_run_data.py
│               ├── configuration/
│               │   ├── __init__.py
│               │   ├── test_indexes_configuration.py
│               │   └── test_profile_data_configuration.py
│               ├── exchanges/
│               │   └── __init__.py
│               ├── orders/
│               │   ├── __init__.py
│               │   ├── order_types/
│               │   │   ├── __init__.py
│               │   │   ├── test_create_order.py
│               │   │   ├── test_limit_order.py
│               │   │   ├── test_market_order.py
│               │   │   ├── test_multiple_orders_creation.py
│               │   │   ├── test_stop_loss_order.py
│               │   │   ├── test_trailing_limit_order.py
│               │   │   ├── test_trailing_market_order.py
│               │   │   └── test_trailing_stop_loss_order.py
│               │   ├── position_size/
│               │   │   ├── __init__.py
│               │   │   └── test_target_position.py
│               │   └── test_cancelling.py
│               ├── static/
│               │   ├── config.json
│               │   └── profile.json
│               └── test_utils/
│                   ├── __init__.py
│                   └── order_util.py
├── README.md
├── Services/
│   ├── Interfaces/
│   │   ├── telegram_bot_interface/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── telegram_bot.py
│   │   │   └── tests/
│   │   │       └── test_bot_interface.py
│   │   └── web_interface/
│   │       ├── __init__.py
│   │       ├── advanced_controllers/
│   │       │   ├── __init__.py
│   │       │   ├── configuration.py
│   │       │   ├── home.py
│   │       │   ├── matrix.py
│   │       │   ├── strategy_optimizer.py
│   │       │   └── tentacles_management.py
│   │       ├── advanced_templates/
│   │       │   ├── advanced_evaluator_config.html
│   │       │   ├── advanced_index.html
│   │       │   ├── advanced_layout.html
│   │       │   ├── advanced_matrix.html
│   │       │   ├── advanced_strategy_optimizer.html
│   │       │   ├── advanced_tentacle_packages.html
│   │       │   └── advanced_tentacles.html
│   │       ├── api/
│   │       │   ├── __init__.py
│   │       │   ├── bots.py
│   │       │   ├── config.py
│   │       │   ├── dsl.py
│   │       │   ├── exchanges.py
│   │       │   ├── feedback.py
│   │       │   ├── metadata.py
│   │       │   ├── tentacles_packages.py
│   │       │   ├── trading.py
│   │       │   ├── user_commands.py
│   │       │   └── webhook.py
│   │       ├── constants.py
│   │       ├── controllers/
│   │       │   ├── __init__.py
│   │       │   ├── about.py
│   │       │   ├── automation.py
│   │       │   ├── backtesting.py
│   │       │   ├── commands.py
│   │       │   ├── community.py
│   │       │   ├── community_authentication.py
│   │       │   ├── configuration.py
│   │       │   ├── dashboard.py
│   │       │   ├── distributions/
│   │       │   │   ├── __init__.py
│   │       │   │   └── market_making/
│   │       │   │       ├── __init__.py
│   │       │   │       ├── cloud.py
│   │       │   │       ├── configuration.py
│   │       │   │       └── dashboard.py
│   │       │   ├── dsl.py
│   │       │   ├── errors.py
│   │       │   ├── home.py
│   │       │   ├── interface_settings.py
│   │       │   ├── logs.py
│   │       │   ├── medias.py
│   │       │   ├── octobot_authentication.py
│   │       │   ├── octobot_help.py
│   │       │   ├── portfolio.py
│   │       │   ├── profiles.py
│   │       │   ├── reboot.py
│   │       │   ├── robots.py
│   │       │   ├── tentacles_config.py
│   │       │   ├── terms.py
│   │       │   ├── trading.py
│   │       │   └── welcome.py
│   │       ├── enums.py
│   │       ├── errors.py
│   │       ├── flask_util/
│   │       │   ├── __init__.py
│   │       │   ├── browsing_data_provider.py
│   │       │   ├── content_types_management.py
│   │       │   ├── context_processor.py
│   │       │   ├── cors.py
│   │       │   ├── file_services.py
│   │       │   ├── json_provider.py
│   │       │   └── template_filters.py
│   │       ├── login/
│   │       │   ├── __init__.py
│   │       │   ├── open_source_package_required.py
│   │       │   ├── user.py
│   │       │   └── web_login_manager.py
│   │       ├── metadata.json
│   │       ├── models/
│   │       │   ├── __init__.py
│   │       │   ├── backtesting.py
│   │       │   ├── commands.py
│   │       │   ├── community.py
│   │       │   ├── configuration.py
│   │       │   ├── dashboard.py
│   │       │   ├── distributions/
│   │       │   │   ├── __init__.py
│   │       │   │   └── market_making/
│   │       │   │       ├── __init__.py
│   │       │   │       └── configuration.py
│   │       │   ├── dsl.py
│   │       │   ├── interface_settings.py
│   │       │   ├── json_schemas.py
│   │       │   ├── logs.py
│   │       │   ├── medias.py
│   │       │   ├── profiles.py
│   │       │   ├── strategy_optimizer.py
│   │       │   ├── tentacles.py
│   │       │   ├── trading.py
│   │       │   └── web_interface_tab.py
│   │       ├── plugins/
│   │       │   ├── __init__.py
│   │       │   ├── abstract_plugin.py
│   │       │   └── plugin_management.py
│   │       ├── security.py
│   │       ├── static/
│   │       │   ├── css/
│   │       │   │   ├── bootstrap-editable.css
│   │       │   │   ├── components/
│   │       │   │   │   └── configuration.css
│   │       │   │   ├── layout.css
│   │       │   │   ├── style.css
│   │       │   │   └── w2ui_template.css
│   │       │   ├── distributions/
│   │       │   │   └── market_making/
│   │       │   │       └── js/
│   │       │   │           ├── configuration.js
│   │       │   │           └── dashboard.js
│   │       │   ├── js/
│   │       │   │   ├── common/
│   │       │   │   │   ├── backtesting_util.js
│   │       │   │   │   ├── bot_connection.js
│   │       │   │   │   ├── candlesticks.js
│   │       │   │   │   ├── common_handlers.js
│   │       │   │   │   ├── cst.js
│   │       │   │   │   ├── custom_elements.js
│   │       │   │   │   ├── data_collector_util.js
│   │       │   │   │   ├── dom_updater.js
│   │       │   │   │   ├── exchange_accounts.js
│   │       │   │   │   ├── feedback.js
│   │       │   │   │   ├── json_editor_settings.js
│   │       │   │   │   ├── on_load.js
│   │       │   │   │   ├── pnl_history.js
│   │       │   │   │   ├── portfolio_history.js
│   │       │   │   │   ├── required.js
│   │       │   │   │   ├── resources_rendering.js
│   │       │   │   │   ├── stepper.js
│   │       │   │   │   ├── tables_display.js
│   │       │   │   │   ├── tracking.js
│   │       │   │   │   ├── tutorial.js
│   │       │   │   │   └── util.js
│   │       │   │   └── components/
│   │       │   │       ├── advanced_matrix.js
│   │       │   │       ├── automations.js
│   │       │   │       ├── backtesting.js
│   │       │   │       ├── commands.js
│   │       │   │       ├── community.js
│   │       │   │       ├── community_metrics.js
│   │       │   │       ├── config_tentacle.js
│   │       │   │       ├── configuration.js
│   │       │   │       ├── dashboard.js
│   │       │   │       ├── dashboard_tutorial_starter.js
│   │       │   │       ├── data_collector.js
│   │       │   │       ├── dsl_help.js
│   │       │   │       ├── evaluator_configuration.js
│   │       │   │       ├── extensions.js
│   │       │   │       ├── logs.js
│   │       │   │       ├── market_status.js
│   │       │   │       ├── navbar.js
│   │       │   │       ├── portfolio.js
│   │       │   │       ├── profile_management.js
│   │       │   │       ├── profiles_selector.js
│   │       │   │       ├── strategy_optimizer.js
│   │       │   │       ├── tentacles_configuration.js
│   │       │   │       ├── trading.js
│   │       │   │       ├── trading_type_selector.js
│   │       │   │       ├── tradingview_email_config.js
│   │       │   │       └── wait_reboot.js
│   │       │   └── license.txt
│   │       ├── templates/
│   │       │   ├── 404.html
│   │       │   ├── 500.html
│   │       │   ├── about.html
│   │       │   ├── accounts.html
│   │       │   ├── automations.html
│   │       │   ├── backtesting.html
│   │       │   ├── community.html
│   │       │   ├── community_login.html
│   │       │   ├── community_metrics.html
│   │       │   ├── community_register.html
│   │       │   ├── components/
│   │       │   │   ├── community/
│   │       │   │   │   ├── bot_selector.html
│   │       │   │   │   ├── bots_stats.html
│   │       │   │   │   ├── cloud_strategies.html
│   │       │   │   │   ├── cloud_strategies_selector.html
│   │       │   │   │   ├── login.html
│   │       │   │   │   ├── octobot_cloud_description.html
│   │       │   │   │   ├── octobot_cloud_features.html
│   │       │   │   │   ├── tentacle_packages.html
│   │       │   │   │   └── user_details.html
│   │       │   │   ├── config/
│   │       │   │   │   ├── currency_card.html
│   │       │   │   │   ├── editable_config.html
│   │       │   │   │   ├── evaluator_card.html
│   │       │   │   │   ├── exchange_card.html
│   │       │   │   │   ├── notification_config.html
│   │       │   │   │   ├── profiles.html
│   │       │   │   │   ├── service_card.html
│   │       │   │   │   ├── tentacle_card.html
│   │       │   │   │   ├── tentacle_config_editor.html
│   │       │   │   │   └── trader_card.html
│   │       │   │   ├── modals/
│   │       │   │   │   ├── generic_modal.html
│   │       │   │   │   └── trading_state_modal.html
│   │       │   │   └── tentacles_packages/
│   │       │   │       └── tentacles_package_card.html
│   │       │   ├── config_tentacle.html
│   │       │   ├── data_collector.html
│   │       │   ├── distributions/
│   │       │   │   ├── default/
│   │       │   │   │   ├── footer.html
│   │       │   │   │   └── navbar.html
│   │       │   │   └── market_making/
│   │       │   │       ├── cloud.html
│   │       │   │       ├── cloud_features.html
│   │       │   │       ├── configuration.html
│   │       │   │       ├── dashboard.html
│   │       │   │       ├── footer.html
│   │       │   │       ├── interfaces.html
│   │       │   │       ├── navbar.html
│   │       │   │       └── portfolio.html
│   │       │   ├── dsl_help.html
│   │       │   ├── extensions.html
│   │       │   ├── index.html
│   │       │   ├── layout.html
│   │       │   ├── login.html
│   │       │   ├── logs.html
│   │       │   ├── macros/
│   │       │   │   ├── backtesting_utils.html
│   │       │   │   ├── cards.html
│   │       │   │   ├── critical_notifications_alert.html
│   │       │   │   ├── flash_messages.html
│   │       │   │   ├── forms.html
│   │       │   │   ├── major_issue_alert.html
│   │       │   │   ├── starting_waiter.html
│   │       │   │   ├── tables.html
│   │       │   │   ├── tentacles.html
│   │       │   │   ├── text.html
│   │       │   │   └── trading_state.html
│   │       │   ├── octobot_help.html
│   │       │   ├── portfolio.html
│   │       │   ├── profile.html
│   │       │   ├── profiles_selector.html
│   │       │   ├── robots.txt
│   │       │   ├── symbol_market_status.html
│   │       │   ├── terms.html
│   │       │   ├── trading.html
│   │       │   ├── trading_type_selector.html
│   │       │   ├── tradingview_email_config.html
│   │       │   ├── wait_reboot.html
│   │       │   └── welcome.html
│   │       ├── tests/
│   │       │   ├── __init__.py
│   │       │   ├── distribution_tester.py
│   │       │   ├── distributions/
│   │       │   │   ├── __init__.py
│   │       │   │   ├── test_default.py
│   │       │   │   └── test_market_making.py
│   │       │   └── plugin_tester.py
│   │       ├── util/
│   │       │   ├── __init__.py
│   │       │   ├── browser_util.py
│   │       │   └── flask_util.py
│   │       ├── web.py
│   │       └── websockets/
│   │           ├── __init__.py
│   │           ├── abstract_websocket_namespace_notifier.py
│   │           ├── backtesting.py
│   │           ├── dashboard.py
│   │           ├── data_collector.py
│   │           ├── notifications.py
│   │           └── strategy_optimizer.py
│   ├── Notifiers/
│   │   ├── telegram_notifier/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── telegram.py
│   │   ├── twitter_notifier/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── twitter.py
│   │   └── web_notifier/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── web.py
│   ├── Services_bases/
│   │   ├── google_service/
│   │   │   ├── __init__.py
│   │   │   ├── google.py
│   │   │   └── metadata.json
│   │   ├── gpt_service/
│   │   │   ├── __init__.py
│   │   │   ├── gpt.py
│   │   │   └── metadata.json
│   │   ├── reddit_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── reddit.py
│   │   ├── telegram_api_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── telegram_api.py
│   │   ├── telegram_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── telegram.py
│   │   ├── trading_view_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── trading_view.py
│   │   ├── twitter_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── twitter.py
│   │   ├── web_service/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── web.py
│   │   └── webhook_service/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       └── webhook.py
│   └── Services_feeds/
│       ├── google_service_feed/
│       │   ├── __init__.py
│       │   ├── google_feed.py
│       │   └── metadata.json
│       ├── reddit_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── reddit_feed.py
│       ├── telegram_api_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── telegram_api_feed.py
│       ├── telegram_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── telegram_feed.py
│       ├── trading_view_service_feed/
│       │   ├── __init__.py
│       │   ├── metadata.json
│       │   └── trading_view_feed.py
│       └── twitter_service_feed/
│           ├── __init__.py
│           ├── metadata.json
│           └── twitter_feed.py
├── Trading/
│   ├── Exchange/
│   │   ├── ascendex/
│   │   │   ├── __init__.py
│   │   │   ├── ascendex_exchange.py
│   │   │   └── metadata.json
│   │   ├── ascendex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── ascendex_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── binance/
│   │   │   ├── __init__.py
│   │   │   ├── binance_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── binance.md
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_sandbox.py
│   │   ├── binance_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── binance_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── binanceus/
│   │   │   ├── __init__.py
│   │   │   ├── binanceus_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── BinanceUS.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── binanceus_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── binanceus_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── bingx/
│   │   │   ├── __init__.py
│   │   │   ├── bingx_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bingx.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bingx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bingx_websocket.py
│   │   │   └── metadata.json
│   │   ├── bitfinex/
│   │   │   ├── __init__.py
│   │   │   ├── bitfinex_exchange.py
│   │   │   └── metadata.json
│   │   ├── bitfinex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bitfinex_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── bitget/
│   │   │   ├── __init__.py
│   │   │   ├── bitget_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitget.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitget_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bitget_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bithumb/
│   │   │   ├── __init__.py
│   │   │   ├── bithumb_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bithumb.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitmart/
│   │   │   ├── __init__.py
│   │   │   ├── bitmart_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── bitmart.md
│   │   ├── bitmart_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bitmart_websocket.py
│   │   │   └── metadata.json
│   │   ├── bitmex/
│   │   │   ├── __init__.py
│   │   │   ├── bitmex_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitmex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitso/
│   │   │   ├── __init__.py
│   │   │   ├── bitso_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitso.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bitstamp/
│   │   │   ├── __init__.py
│   │   │   ├── bitstamp_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bitstamp.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bybit/
│   │   │   ├── __init__.py
│   │   │   ├── bybit_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── bybit.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── bybit_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── bybit_websocket.py
│   │   │   └── metadata.json
│   │   ├── coinbase/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_exchange.py
│   │   │   └── metadata.json
│   │   ├── coinbase_pro/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_pro_exchange.py
│   │   │   └── metadata.json
│   │   ├── coinbase_pro_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_pro_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── coinbase_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── coinbase_websocket.py
│   │   │   └── metadata.json
│   │   ├── coinex/
│   │   │   ├── __init__.py
│   │   │   ├── coinex_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── coinex.md
│   │   ├── coinex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── coinex_websocket.py
│   │   │   └── metadata.json
│   │   ├── configurable_default_ccxt_rest/
│   │   │   ├── __init__.py
│   │   │   ├── configurable_default_rest_ccxt_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── configurable_default_rest_ccxt_exchange.md
│   │   ├── cryptocom/
│   │   │   ├── __init__.py
│   │   │   ├── cryptocom_exchange.py
│   │   │   └── metadata.json
│   │   ├── cryptocom_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── cryptocom_websocket.py
│   │   │   └── metadata.json
│   │   ├── gateio/
│   │   │   ├── __init__.py
│   │   │   ├── gateio_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── gateio.md
│   │   ├── gateio_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── gateio_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── hitbtc/
│   │   │   ├── __init__.py
│   │   │   ├── hitbtc_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hitbtc.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hollaex/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── hollaex.json
│   │   │   ├── hollaex_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hollaex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hollaex_autofilled/
│   │   │   ├── __init__.py
│   │   │   ├── config/
│   │   │   │   └── HollaexAutofilled.json
│   │   │   ├── hollaex_autofilled_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hollaex_autofilled.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hollaex_autofilled_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── hollaex_autofilled_websocket.py
│   │   │   └── metadata.json
│   │   ├── hollaex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── hollaex_websocket.py
│   │   │   └── metadata.json
│   │   ├── htx/
│   │   │   ├── __init__.py
│   │   │   ├── htx_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── htx.md
│   │   ├── htx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── htx_websocket.py
│   │   │   └── metadata.json
│   │   ├── huobi/
│   │   │   ├── __init__.py
│   │   │   ├── huobi_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── huobi.md
│   │   ├── huobi_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── huobi_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── hyperliquid/
│   │   │   ├── __init__.py
│   │   │   ├── hyperliquid_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── hyperliquid.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── hyperliquid_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── hyperliquid_websocket.py
│   │   │   └── metadata.json
│   │   ├── kraken/
│   │   │   ├── __init__.py
│   │   │   ├── kraken_exchange.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── kraken.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── kraken_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── kraken_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── kucoin/
│   │   │   ├── __init__.py
│   │   │   ├── kucoin_exchange.py
│   │   │   └── metadata.json
│   │   ├── kucoin_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── kucoin_websocket.py
│   │   │   ├── metadata.json
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── lbank/
│   │   │   ├── __init__.py
│   │   │   ├── lbank_exchange.py
│   │   │   ├── metadata.json
│   │   │   └── resources/
│   │   │       └── lbank.md
│   │   ├── lbank_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── lbank_websocket.py
│   │   │   └── metadata.json
│   │   ├── mexc/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── mexc_exchange.py
│   │   ├── mexc_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── mexc_websocket.py
│   │   ├── myokx/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── myokx_exchange.py
│   │   │   └── resources/
│   │   │       └── myokx.md
│   │   ├── myokx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── myokx_websocket.py
│   │   ├── ndax/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── ndax_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── ndax.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── okcoin/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okcoin_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── okcoin.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── okx/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okx_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── okx.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── okx_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okx_websocket.py
│   │   │   └── tests/
│   │   │       ├── __init__.py
│   │   │       └── test_unauthenticated_mocked_feeds.py
│   │   ├── okxus/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── okxus_exchange.py
│   │   │   └── resources/
│   │   │       └── okxus.md
│   │   ├── okxus_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   └── okxus_websocket.py
│   │   ├── phemex/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── phemex_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── phemex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── phemex_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── phemex_websocket.py
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── poloniex/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── poloniex_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── poloniex.md
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── polymarket/
│   │   │   ├── __init__.py
│   │   │   ├── ccxt/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── polymarket_abstract.py
│   │   │   │   ├── polymarket_async.py
│   │   │   │   ├── polymarket_pro.py
│   │   │   │   └── polymarket_sync.py
│   │   │   ├── metadata.json
│   │   │   ├── polymarket_exchange.py
│   │   │   ├── resources/
│   │   │   │   └── Polymarket.md
│   │   │   ├── script/
│   │   │   │   ├── __init__.py
│   │   │   │   └── download.py
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── polymarket_websocket_feed/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── polymarket_websocket.py
│   │   │   └── tests/
│   │   │       └── __init__.py
│   │   ├── upbitexchange/
│   │   │   ├── __init__.py
│   │   │   ├── metadata.json
│   │   │   ├── resources/
│   │   │   │   └── upbitexchange.md
│   │   │   ├── tests/
│   │   │   │   └── __init__.py
│   │   │   └── upbit_exchange.py
│   │   └── wavesexchange/
│   │       ├── __init__.py
│   │       ├── metadata.json
│   │       ├── resources/
│   │       │   └── wavesexchange.md
│   │       ├── tests/
│   │       │   └── __init__.py
│   │       └── wavesexchange_exchange.py
│   └── Mode/
│       ├── arbitrage_trading_mode/
│       │   ├── __init__.py
│       │   ├── arbitrage_container.py
│       │   ├── arbitrage_trading.py
│       │   ├── config/
│       │   │   └── ArbitrageTradingMode.json
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── ArbitrageTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_arbitrage_container.py
│       │       ├── test_arbitrage_trading_mode_consumer.py
│       │       └── test_arbitrage_trading_mode_producer.py
│       ├── blank_trading_mode/
│       │   ├── __init__.py
│       │   ├── blank_trading.py
│       │   ├── config/
│       │   │   └── BlankTradingMode.json
│       │   ├── metadata.json
│       │   └── resources/
│       │       └── BlankTradingMode.md
│       ├── daily_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── DailyTradingMode.json
│       │   ├── daily_trading.pxd
│       │   ├── daily_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── DailyTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_daily_trading_mode.py
│       │       ├── test_daily_trading_mode_consumer.py
│       │       └── test_daily_trading_mode_producer.py
│       ├── dca_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── DCATradingMode.json
│       │   ├── dca_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── DCATradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       └── test_dca_trading_mode.py
│       ├── dip_analyser_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── DipAnalyserTradingMode.json
│       │   ├── dip_analyser_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── DipAnalyserTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       └── test_dip_analyser_trading_mode.py
│       ├── grid_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── GridTradingMode.json
│       │   ├── grid_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── GridTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── open_orders_data.py
│       │       └── test_grid_trading_mode.py
│       ├── index_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── IndexTradingMode.json
│       │   ├── index_distribution.py
│       │   ├── index_trading.py
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── IndexTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_index_distribution.py
│       │       └── test_index_trading_mode.py
│       ├── market_making_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── MarketMakingTradingMode.json
│       │   ├── market_making_trading.py
│       │   ├── metadata.json
│       │   ├── order_book_distribution.py
│       │   ├── reference_price.py
│       │   ├── resources/
│       │   │   └── MarketMakingTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_market_making_trading.py
│       │       └── test_order_book_distribution.py
│       ├── remote_trading_signals_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── RemoteTradingSignalsTradingMode.json
│       │   ├── metadata.json
│       │   ├── remote_trading_signals_trading.py
│       │   ├── resources/
│       │   │   └── RemoteTradingSignalsTradingMode.md
│       │   └── tests/
│       │       ├── __init__.py
│       │       ├── test_remote_trading_signals_trading_consumer.py
│       │       └── test_remote_trading_signals_trading_producer.py
│       ├── signal_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── SignalTradingMode.json
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── SignalTradingMode.md
│       │   └── signal_trading.py
│       ├── staggered_orders_trading_mode/
│       │   ├── __init__.py
│       │   ├── config/
│       │   │   └── StaggeredOrdersTradingMode.json
│       │   ├── metadata.json
│       │   ├── resources/
│       │   │   └── StaggeredOrdersTradingMode.md
│       │   ├── staggered_orders_trading.py
│       │   └── tests/
│       │       ├── __init__.py
│       │       └── test_staggered_orders_trading_mode.py
│       └── trading_view_signals_trading_mode/
│           ├── __init__.py
│           ├── config/
│           │   └── TradingViewSignalsTradingMode.json
│           ├── metadata.json
│           ├── resources/
│           │   └── TradingViewSignalsTradingMode.md
│           ├── tests/
│           │   ├── __init__.py
│           │   └── test_trading_view_signals_trading.py
│           └── trading_view_signals_trading.py
├── metadata.yaml
├── octobot_config.json
├── profiles/
│   ├── arbitrage_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── ArbitrageTradingMode.json
│   │   └── tentacles_config.json
│   ├── copy_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── RemoteTradingSignalsTradingMode.json
│   │   └── tentacles_config.json
│   ├── daily_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DailyTradingMode.json
│   │   │   └── SimpleStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── dip_analyser/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DipAnalyserStrategyEvaluator.json
│   │   │   ├── DipAnalyserTradingMode.json
│   │   │   ├── InstantFluctuationsEvaluator.json
│   │   │   └── RSIWeightMomentumEvaluator.json
│   │   └── tentacles_config.json
│   ├── gpt_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DCATradingMode.json
│   │   │   ├── GPTEvaluator.json
│   │   │   └── SimpleStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── grid_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── GridTradingMode.json
│   │   └── tentacles_config.json
│   ├── index_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── IndexTradingMode.json
│   │   └── tentacles_config.json
│   ├── market_making/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── MarketMakingTradingMode.json
│   │   └── tentacles_config.json
│   ├── non-trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── BlankStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── signal_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── MoveSignalsStrategyEvaluator.json
│   │   │   └── SignalTradingMode.json
│   │   └── tentacles_config.json
│   ├── simple_dca/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── DCATradingMode.json
│   │   └── tentacles_config.json
│   ├── smart_dca/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   ├── DCATradingMode.json
│   │   │   ├── EMAMomentumEvaluator.json
│   │   │   └── SimpleStrategyEvaluator.json
│   │   └── tentacles_config.json
│   ├── staggered_orders_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── StaggeredOrdersTradingMode.json
│   │   └── tentacles_config.json
│   ├── tradingview_trading/
│   │   ├── profile.json
│   │   ├── specific_config/
│   │   │   └── TradingViewSignalsTradingMode.json
│   │   └── tentacles_config.json
│   └── trailing_grid_trading/
│       ├── profile.json
│       ├── specific_config/
│       │   └── GridTradingMode.json
│       └── tentacles_config.json
└── scripts/
    └── clear_cloudflare_cache.py
Download .txt
Showing preview only (406K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3968 symbols across 395 files)

FILE: Automation/actions/cancel_open_order_action/cancel_open_orders.py
  class CancelOpenOrders (line 23) | class CancelOpenOrders(abstract_action.AbstractAction):
    method process (line 24) | async def process(self):
    method get_description (line 32) | def get_description() -> str:
    method get_user_inputs (line 35) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 38) | def apply_config(self, config):

FILE: Automation/actions/sell_all_currencies_action/sell_all_currencies.py
  class SellAllCurrencies (line 23) | class SellAllCurrencies(abstract_action.AbstractAction):
    method process (line 24) | async def process(self):
    method get_description (line 32) | def get_description() -> str:
    method get_user_inputs (line 35) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 38) | def apply_config(self, config):

FILE: Automation/actions/send_notification_action/send_notification.py
  class SendNotification (line 23) | class SendNotification(abstract_action.AbstractAction):
    method __init__ (line 26) | def __init__(self):
    method process (line 30) | async def process(self):
    method get_description (line 39) | def get_description() -> str:
    method get_user_inputs (line 44) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 53) | def apply_config(self, config):

FILE: Automation/actions/stop_trading_action/stop_trading.py
  class StopTrading (line 21) | class StopTrading(cancel_open_orders.CancelOpenOrders):
    method process (line 24) | async def process(self):
    method get_description (line 35) | def get_description() -> str:

FILE: Automation/conditions/no_condition_condition/no_condition.py
  class NoCondition (line 20) | class NoCondition(abstract_condition.AbstractCondition):
    method evaluate (line 21) | async def evaluate(self) -> bool:
    method get_description (line 25) | def get_description() -> str:
    method get_user_inputs (line 28) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 31) | def apply_config(self, config):

FILE: Automation/conditions/scripted_condition/scripted_condition.py
  class ScriptedCondition (line 27) | class ScriptedCondition(abstract_condition.AbstractCondition):
    method __init__ (line 31) | def __init__(self):
    method evaluate (line 38) | async def evaluate(self) -> bool:
    method get_description (line 45) | def get_description() -> str:
    method get_user_inputs (line 48) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 64) | def apply_config(self, config):
    method _validate_script (line 73) | def _validate_script(self):
    method _create_dsl_interpreter (line 83) | def _create_dsl_interpreter(self):
    method _get_exchange_manager (line 98) | def _get_exchange_manager(self):

FILE: Automation/trigger_events/period_check_event/period_check.py
  class PeriodicCheck (line 23) | class PeriodicCheck(abstract_trigger_event.AbstractTriggerEvent):
    method __init__ (line 26) | def __init__(self):
    method stop (line 31) | async def stop(self):
    method _get_next_event (line 36) | async def _get_next_event(self):
    method get_description (line 43) | def get_description() -> str:
    method get_user_inputs (line 46) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 55) | def apply_config(self, config):

FILE: Automation/trigger_events/price_threshold_event/price_threshold.py
  class PriceThreshold (line 28) | class PriceThreshold(abstract_trigger_event.AbstractTriggerEvent):
    method __init__ (line 34) | def __init__(self):
    method _register_consumer (line 44) | async def _register_consumer(self):
    method mark_price_callback (line 58) | async def mark_price_callback(
    method _update_last_price (line 67) | def _update_last_price(self, mark_price):
    method _check_threshold (line 70) | def _check_threshold(self, mark_price):
    method stop (line 77) | async def stop(self):
    method _get_next_event (line 85) | async def _get_next_event(self):
    method get_description (line 95) | def get_description() -> str:
    method get_user_inputs (line 98) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 124) | def apply_config(self, config):

FILE: Automation/trigger_events/profitability_threshold_event/profitability_threshold.py
  class ProfitabilityThreshold (line 32) | class ProfitabilityThreshold(abstract_trigger_event.AbstractTriggerEvent):
    method __init__ (line 38) | def __init__(self):
    method _register_consumer (line 48) | async def _register_consumer(self):
    method balance_profitability_callback (line 61) | async def balance_profitability_callback(
    method _update_profitability_by_time (line 76) | def _update_profitability_by_time(self, profitability_percent):
    method _check_threshold (line 83) | def _check_threshold(self, profitability_percent):
    method stop (line 92) | async def stop(self):
    method _get_next_event (line 100) | async def _get_next_event(self):
    method get_description (line 110) | def get_description() -> str:
    method get_user_inputs (line 115) | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: ...
    method apply_config (line 142) | def apply_config(self, config):

FILE: Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/bot_snapshot_with_history_collector.py
  class ExchangeBotSnapshotWithHistoryCollector (line 41) | class ExchangeBotSnapshotWithHistoryCollector(collector.AbstractExchange...
    method __init__ (line 46) | def __init__(self, config, exchange_name, exchange_type, tentacles_set...
    method get_permanent_file_identifier (line 69) | def get_permanent_file_identifier(self):
    method initialize (line 75) | async def initialize(self):
    method set_file_path (line 80) | def set_file_path(self) -> None:
    method finalize_database (line 85) | def finalize_database(self):
    method _check_database_content (line 90) | async def _check_database_content(self):
    method start (line 111) | async def start(self):
    method stop (line 177) | async def stop(self, should_stop_database=True):
    method _update_description (line 188) | async def _update_description(self):
    method get_ticker_history (line 203) | async def get_ticker_history(self, exchange, symbol):
    method get_order_book_history (line 206) | async def get_order_book_history(self, exchange, symbol):
    method get_recent_trades_history (line 209) | async def get_recent_trades_history(self, exchange, symbol):
    method get_ohlcv_snapshot (line 212) | def get_ohlcv_snapshot(self, symbol, time_frame):
    method collect_historical_ohlcv (line 227) | async def collect_historical_ohlcv(self, exchange, symbol, time_frame,...
    method find_candle (line 250) | def find_candle(self, candles, timestamp):
    method update_ohlcv (line 256) | async def update_ohlcv(self, exchange, symbol, time_frame, time_frame_...
    method _check_ohlcv_integrity (line 287) | async def _check_ohlcv_integrity(self, database_candles):
    method get_ohlcv_history (line 299) | async def get_ohlcv_history(self, exchange, symbol, time_frame):
    method _import_candles_from_datafile (line 381) | async def _import_candles_from_datafile(self, exchange, symbol, time_f...
    method get_kline_history (line 389) | async def get_kline_history(self, exchange, symbol, time_frame):
    method adapt_timestamps (line 392) | async def adapt_timestamps(self):
    method get_fetch_data_id (line 412) | def get_fetch_data_id(self, symbol, timeframe):
    method get_first_candle_timestamp (line 415) | async def get_first_candle_timestamp(self, ideal_start_timestamp, symb...

FILE: Backtesting/collectors/exchanges/exchange_history_collector/history_collector.py
  class ExchangeHistoryDataCollector (line 36) | class ExchangeHistoryDataCollector(collector.AbstractExchangeHistoryColl...
    method __init__ (line 39) | def __init__(self, config, exchange_name, exchange_type, tentacles_set...
    method start (line 50) | async def start(self):
    method _load_all_available_timeframes (line 101) | def _load_all_available_timeframes(self):
    method stop (line 107) | async def stop(self, should_stop_database=True):
    method get_ticker_history (line 119) | async def get_ticker_history(self, exchange, symbol):
    method get_order_book_history (line 122) | async def get_order_book_history(self, exchange, symbol):
    method get_recent_trades_history (line 125) | async def get_recent_trades_history(self, exchange, symbol):
    method get_ohlcv_history (line 128) | async def get_ohlcv_history(self, exchange, symbol, time_frame):
    method get_kline_history (line 170) | async def get_kline_history(self, exchange, symbol, time_frame):
    method check_timestamps (line 173) | async def check_timestamps(self):
    method get_first_candle_timestamp (line 186) | async def get_first_candle_timestamp(self, ideal_start_timestamp, symb...

FILE: Backtesting/collectors/exchanges/exchange_history_collector/tests/test_history_collector.py
  function data_collector (line 41) | async def data_collector(exchange_name, tentacles_setup_config, symbols,...
  function collector_database (line 61) | async def collector_database(collector):
  function test_collect_valid_data (line 70) | async def test_collect_valid_data():
  function test_collect_invalid_data (line 96) | async def test_collect_invalid_data():
  function test_collect_valid_date_range (line 110) | async def test_collect_valid_date_range():
  function test_collect_invalid_date_range (line 174) | async def test_collect_invalid_date_range():
  function test_collect_multi_pair (line 192) | async def test_collect_multi_pair():
  function test_stop_collect (line 224) | async def test_stop_collect():

FILE: Backtesting/collectors/exchanges/exchange_live_collector/live_collector.py
  class ExchangeLiveDataCollector (line 31) | class ExchangeLiveDataCollector(exchanges.AbstractExchangeLiveCollector):
    method start (line 34) | async def start(self):
    method ticker_callback (line 63) | async def ticker_callback(self, exchange: str, exchange_id: str,
    method order_book_callback (line 69) | async def order_book_callback(self, exchange: str, exchange_id: str,
    method recent_trades_callback (line 76) | async def recent_trades_callback(self, exchange: str, exchange_id: str,
    method ohlcv_callback (line 83) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method kline_callback (line 90) | async def kline_callback(self, exchange: str, exchange_id: str,

FILE: Backtesting/converters/exchanges/legacy_data_converter/legacy_converter.py
  class LegacyDataConverter (line 33) | class LegacyDataConverter(converters.DataConverter):
    class PriceIndexes (line 41) | class PriceIndexes(enum.Enum):
    method __init__ (line 49) | def __init__(self, backtesting_file_to_convert):
    method can_convert (line 59) | async def can_convert(self, ) -> bool:
    method convert (line 79) | async def convert(self) -> bool:
    method _create_description (line 95) | async def _create_description(self):
    method _convert_ohlcv (line 104) | async def _convert_ohlcv(self, time_frame):
    method _get_formatted_candles (line 113) | def _get_formatted_candles(self, time_frame):
    method _read_data_file (line 132) | def _read_data_file(self):
    method _interpret_file_name (line 146) | def _interpret_file_name(file_name):

FILE: Backtesting/importers/exchanges/generic_exchange_importer/generic_exchange_importer.py
  class GenericExchangeDataImporter (line 19) | class GenericExchangeDataImporter(importers.ExchangeDataImporter):

FILE: Evaluator/RealTime/instant_fluctuations_evaluator/instant_fluctuations.py
  class InstantFluctuationsEvaluator (line 27) | class InstantFluctuationsEvaluator(evaluators.RealTimeEvaluator):
    method __init__ (line 38) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 57) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 76) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method kline_callback (line 97) | async def kline_callback(self, exchange: str, exchange_id: str,
    method _trigger_evaluation (line 103) | async def _trigger_evaluation(self, cryptocurrency, symbol, time):
    method evaluate_volume_fluctuations (line 114) | def evaluate_volume_fluctuations(self):
    method start (line 150) | async def start(self, bot_id: str) -> bool:
    method set_default_config (line 172) | def set_default_config(self):
    method get_is_symbol_wildcard (line 177) | def get_is_symbol_wildcard(cls) -> bool:
  class InstantMAEvaluator (line 184) | class InstantMAEvaluator(evaluators.RealTimeEvaluator):
    method __init__ (line 186) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 194) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 212) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method kline_callback (line 228) | async def kline_callback(self, exchange: str, exchange_id: str,
    method _evaluate_current_price (line 237) | async def _evaluate_current_price(self, last_price, cryptocurrency, sy...
    method start (line 263) | async def start(self, bot_id: str) -> bool:
    method set_default_config (line 283) | def set_default_config(self):
    method _compare_data (line 288) | def _compare_data(new_data, old_data):

FILE: Evaluator/Social/forum_evaluator/forum.py
  class RedditForumEvaluator (line 32) | class RedditForumEvaluator(evaluators.SocialEvaluator):
    method __init__ (line 35) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 43) | def init_user_inputs(self, inputs: dict) -> None:
    method _init_cryptocurrencies (line 61) | def _init_cryptocurrencies(self, inputs, cryptocurrency, subreddits):
    method get_is_cryptocurrencies_wildcard (line 77) | def get_is_cryptocurrencies_wildcard(cls) -> bool:
    method get_is_cryptocurrency_name_wildcard (line 84) | def get_is_cryptocurrency_name_wildcard(cls) -> bool:
    method _print_entry (line 90) | def _print_entry(self, entry_text, entry_note, count=""):
    method _feed_callback (line 94) | async def _feed_callback(self, data):
    method _get_sentiment (line 106) | def _get_sentiment(self, entry):
    method _is_interested_by_this_notification (line 114) | def _is_interested_by_this_notification(self, notification_description):
    method _get_config_elements (line 124) | def _get_config_elements(self, config_cryptocurrencies, key):
    method prepare (line 133) | async def prepare(self):

FILE: Evaluator/Social/news_evaluator/news.py
  class TwitterNewsEvaluator (line 29) | class TwitterNewsEvaluator:
    method __init__ (line 37) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 45) | def init_user_inputs(self, inputs: dict) -> None:
    method _init_cryptocurrencies (line 67) | def _init_cryptocurrencies(self, inputs, cryptocurrency, accounts, has...
    method get_is_cryptocurrencies_wildcard (line 90) | def get_is_cryptocurrencies_wildcard(cls) -> bool:
    method get_is_cryptocurrency_name_wildcard (line 97) | def get_is_cryptocurrency_name_wildcard(cls) -> bool:
    method _print_tweet (line 103) | def _print_tweet(self, tweet_text, tweet_url, note, count=""):
    method _feed_callback (line 107) | async def _feed_callback(self, data):
    method _check_eval_note (line 118) | async def _check_eval_note(self, note):
    method _compute_notification_time_to_live (line 126) | def _compute_notification_time_to_live(evaluation):
    method _get_tweet_sentiment (line 129) | def _get_tweet_sentiment(self, tweet, tweet_text, is_a_quote=False):
    method _is_interested_by_this_notification (line 147) | def _is_interested_by_this_notification(self, notification_description):
    method _get_config_elements (line 170) | def _get_config_elements(self, config_cryptocurrencies, key):
    method prepare (line 179) | async def prepare(self):

FILE: Evaluator/Social/signal_evaluator/signal.py
  class TelegramSignalEvaluator (line 25) | class TelegramSignalEvaluator(evaluators.SocialEvaluator):
    method init_user_inputs (line 28) | def init_user_inputs(self, inputs: dict) -> None:
    method _feed_callback (line 35) | async def _feed_callback(self, data):
    method _is_interested_by_this_notification (line 45) | def _is_interested_by_this_notification(self, notification_description):
    method analyse_notification (line 51) | async def analyse_notification(self, notification):
    method get_is_cryptocurrencies_wildcard (line 74) | def get_is_cryptocurrencies_wildcard(cls) -> bool:
    method get_is_cryptocurrency_name_wildcard (line 81) | def get_is_cryptocurrency_name_wildcard(cls) -> bool:
    method get_is_symbol_wildcard (line 88) | def get_is_symbol_wildcard(cls) -> bool:
    method _get_tentacle_registration_topic (line 94) | def _get_tentacle_registration_topic(self, all_symbols_by_crypto_curre...
  class TelegramChannelSignalEvaluator (line 108) | class TelegramChannelSignalEvaluator(evaluators.SocialEvaluator):
    method __init__ (line 117) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 121) | def init_user_inputs(self, inputs: dict) -> None:
    method _init_channel_config (line 136) | def _init_channel_config(self, inputs, channel_name, signal_pair, buy_...
    method _init_pattern_config (line 158) | def _init_pattern_config(self, inputs, buy_regex, sell_regex):
    method _feed_callback (line 174) | async def _feed_callback(self, data):
    method _get_signal_message (line 201) | def _get_signal_message(self, expected_pattern, message):

FILE: Evaluator/Social/signal_evaluator/tests/test_telegram_channel_signal_evaluator.py
  function _trigger_callback_with_data_and_assert_note (line 28) | async def _trigger_callback_with_data_and_assert_note(evaluator: Social....
  function _create_evaluator_with_supported_channel_signals (line 36) | def _create_evaluator_with_supported_channel_signals():
  function test_without_data (line 64) | async def test_without_data():
  function test_with_empty_data (line 69) | async def test_with_empty_data():
  function test_incorrect_signal_without_sender_without_channel_message (line 74) | async def test_incorrect_signal_without_sender_without_channel_message():
  function test_incorrect_signal_without_sender_with_channel_message (line 83) | async def test_incorrect_signal_without_sender_with_channel_message():
  function test_incorrect_signal_chan1_without_content (line 92) | async def test_incorrect_signal_chan1_without_content():
  function test_incorrect_signal_chan1_without_coin (line 101) | async def test_incorrect_signal_chan1_without_coin():
  function test_incorrect_signal_chan1_without_separator (line 115) | async def test_incorrect_signal_chan1_without_separator():
  function test_correct_signal_chan1_with_not_channel_message (line 129) | async def test_correct_signal_chan1_with_not_channel_message():
  function test_correct_signal_chan1_with_chan2 (line 143) | async def test_correct_signal_chan1_with_chan2():
  function test_correct_signal_chan1 (line 157) | async def test_correct_signal_chan1():
  function test_correct_signal_chan2_but_with_chan1 (line 171) | async def test_correct_signal_chan2_but_with_chan1():
  function test_correct_signal_chan2 (line 180) | async def test_correct_signal_chan2():

FILE: Evaluator/Social/trends_evaluator/trends.py
  class GoogleTrendsEvaluator (line 27) | class GoogleTrendsEvaluator(evaluators.SocialEvaluator):
    method __init__ (line 30) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 36) | def init_user_inputs(self, inputs: dict) -> None:
    method get_is_cryptocurrencies_wildcard (line 51) | def get_is_cryptocurrencies_wildcard(cls) -> bool:
    method get_is_cryptocurrency_name_wildcard (line 58) | def get_is_cryptocurrency_name_wildcard(cls) -> bool:
    method _feed_callback (line 64) | async def _feed_callback(self, data):
    method _is_interested_by_this_notification (line 71) | def _is_interested_by_this_notification(self, notification_description):
    method _build_trend_topics (line 74) | def _build_trend_topics(self):
    method prepare (line 82) | async def prepare(self):

FILE: Evaluator/Strategies/blank_strategy_evaluator/blank_strategy.py
  class BlankStrategyEvaluator (line 22) | class BlankStrategyEvaluator(evaluators.StrategyEvaluator):
    method init_user_inputs (line 24) | def init_user_inputs(self, inputs: dict) -> None:
    method get_full_cycle_evaluator_types (line 35) | def get_full_cycle_evaluator_types(self) -> tuple:
    method matrix_callback (line 39) | async def matrix_callback(self,

FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/dip_analyser_strategy.py
  class DipAnalyserStrategyEvaluator (line 31) | class DipAnalyserStrategyEvaluator(evaluators.StrategyEvaluator):
    method get_eval_type (line 36) | def get_eval_type():
    method __init__ (line 39) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 43) | def init_user_inputs(self, inputs: dict) -> None:
    method matrix_callback (line 58) | async def matrix_callback(self,

FILE: Evaluator/Strategies/dip_analyser_strategy_evaluator/tests/test_dip_analyser_strategy_evaluator.py
  function strategy_tester (line 28) | def strategy_tester():
  class DipAnalyserStrategiesEvaluatorTest (line 34) | class DipAnalyserStrategiesEvaluatorTest(abstract_strategy_test.Abstract...
    method test_default_run (line 49) | async def test_default_run(self):
    method test_slow_downtrend (line 53) | async def test_slow_downtrend(self):
    method test_sharp_downtrend (line 58) | async def test_sharp_downtrend(self):
    method test_flat_markets (line 62) | async def test_flat_markets(self):
    method test_slow_uptrend (line 67) | async def test_slow_uptrend(self):
    method test_sharp_uptrend (line 72) | async def test_sharp_uptrend(self):
    method test_up_then_down (line 77) | async def test_up_then_down(self):
  function test_default_run (line 81) | async def test_default_run(strategy_tester):
  function test_slow_downtrend (line 85) | async def test_slow_downtrend(strategy_tester):
  function test_sharp_downtrend (line 89) | async def test_sharp_downtrend(strategy_tester):
  function test_flat_markets (line 93) | async def test_flat_markets(strategy_tester):
  function test_slow_uptrend (line 97) | async def test_slow_uptrend(strategy_tester):
  function test_sharp_uptrend (line 101) | async def test_sharp_uptrend(strategy_tester):
  function test_up_then_down (line 105) | async def test_up_then_down(strategy_tester):

FILE: Evaluator/Strategies/mixed_strategies_evaluator/mixed_strategies.py
  class SimpleStrategyEvaluator (line 34) | class SimpleStrategyEvaluator(evaluators.StrategyEvaluator):
    method __init__ (line 39) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 47) | def init_user_inputs(self, inputs: dict) -> None:
    method get_default_config (line 87) | def get_default_config(cls, time_frames: typing.Optional[list[str]] = ...
    method matrix_callback (line 98) | async def matrix_callback(self,
    method _trigger_evaluation (line 130) | async def _trigger_evaluation(self,
  class TechnicalAnalysisStrategyEvaluator (line 245) | class TechnicalAnalysisStrategyEvaluator(evaluators.StrategyEvaluator):
    method __init__ (line 251) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 261) | def init_user_inputs(self, inputs: dict) -> None:
    method _init_tf_and_weight (line 280) | def _init_tf_and_weight(self, inputs, timeframe, weight):
    method matrix_callback (line 297) | async def matrix_callback(self,
    method _get_weight_by_time_frames (line 359) | def _get_weight_by_time_frames(tf_to_weight):

FILE: Evaluator/Strategies/mixed_strategies_evaluator/tests/test_simple_strategy_evaluator.py
  function strategy_tester (line 28) | def strategy_tester():
  class SimpleStrategyEvaluatorTest (line 34) | class SimpleStrategyEvaluatorTest(abstract_strategy_test.AbstractStrateg...
    method test_default_run (line 41) | async def test_default_run(self):
    method test_slow_downtrend (line 45) | async def test_slow_downtrend(self):
    method test_sharp_downtrend (line 53) | async def test_sharp_downtrend(self):
    method test_flat_markets (line 58) | async def test_flat_markets(self):
    method test_slow_uptrend (line 66) | async def test_slow_uptrend(self):
    method test_sharp_uptrend (line 71) | async def test_sharp_uptrend(self):
    method test_up_then_down (line 76) | async def test_up_then_down(self):
  function test_default_run (line 81) | async def test_default_run(strategy_tester):
  function test_slow_downtrend (line 85) | async def test_slow_downtrend(strategy_tester):
  function test_sharp_downtrend (line 89) | async def test_sharp_downtrend(strategy_tester):
  function test_flat_markets (line 93) | async def test_flat_markets(strategy_tester):
  function test_slow_uptrend (line 97) | async def test_slow_uptrend(strategy_tester):
  function test_sharp_uptrend (line 101) | async def test_sharp_uptrend(strategy_tester):
  function test_up_then_down (line 105) | async def test_up_then_down(strategy_tester):

FILE: Evaluator/Strategies/mixed_strategies_evaluator/tests/test_technical_analysis_strategy_evaluator.py
  function strategy_tester (line 28) | def strategy_tester():
  class TechnicalAnalysisStrategyEvaluatorTest (line 34) | class TechnicalAnalysisStrategyEvaluatorTest(abstract_strategy_test.Abst...
    method test_default_run (line 41) | async def test_default_run(self):
    method test_slow_downtrend (line 45) | async def test_slow_downtrend(self):
    method test_sharp_downtrend (line 53) | async def test_sharp_downtrend(self):
    method test_flat_markets (line 58) | async def test_flat_markets(self):
    method test_slow_uptrend (line 66) | async def test_slow_uptrend(self):
    method test_sharp_uptrend (line 71) | async def test_sharp_uptrend(self):
    method test_up_then_down (line 76) | async def test_up_then_down(self):
  function test_default_run (line 81) | async def test_default_run(strategy_tester):
  function test_slow_downtrend (line 85) | async def test_slow_downtrend(strategy_tester):
  function test_sharp_downtrend (line 89) | async def test_sharp_downtrend(strategy_tester):
  function test_flat_markets (line 93) | async def test_flat_markets(strategy_tester):
  function test_slow_uptrend (line 97) | async def test_slow_uptrend(strategy_tester):
  function test_sharp_uptrend (line 101) | async def test_sharp_uptrend(strategy_tester):
  function test_up_then_down (line 105) | async def test_up_then_down(strategy_tester):

FILE: Evaluator/Strategies/move_signals_strategy_evaluator/move_signals_strategy.py
  class MoveSignalsStrategyEvaluator (line 28) | class MoveSignalsStrategyEvaluator(evaluators.StrategyEvaluator):
    method __init__ (line 38) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 48) | def init_user_inputs(self, inputs: dict) -> None:
    method matrix_callback (line 55) | async def matrix_callback(self,
    method _compute_final_evaluation (line 103) | def _compute_final_evaluation(self):
    method _compute_fractal_evaluation (line 112) | def _compute_fractal_evaluation(signal_with_weight, multiplier):
    method _refresh_evaluations (line 124) | def _refresh_evaluations(self, TA_by_timeframe):
    method _get_tentacle_registration_topic (line 128) | def _get_tentacle_registration_topic(self, all_symbols_by_crypto_curre...
    method _register_time_frame (line 138) | def _register_time_frame(self, time_frame, weight):
  class SignalWithWeight (line 147) | class SignalWithWeight:
    method __init__ (line 149) | def __init__(self, time_frame):
    method reset_evaluation (line 154) | def reset_evaluation(self):
    method refresh_evaluation (line 158) | def refresh_evaluation(self, TA_by_timeframe):

FILE: Evaluator/Strategies/move_signals_strategy_evaluator/tests/test_move_signals_strategy_evaluator.py
  function strategy_tester (line 28) | def strategy_tester():
  class MoveSignalsStrategyEvaluatorTest (line 34) | class MoveSignalsStrategyEvaluatorTest(abstract_strategy_test.AbstractSt...
    method test_default_run (line 41) | async def test_default_run(self):
    method test_slow_downtrend (line 45) | async def test_slow_downtrend(self):
    method test_sharp_downtrend (line 53) | async def test_sharp_downtrend(self):
    method test_flat_markets (line 58) | async def test_flat_markets(self):
    method test_slow_uptrend (line 66) | async def test_slow_uptrend(self):
    method test_sharp_uptrend (line 71) | async def test_sharp_uptrend(self):
    method test_up_then_down (line 76) | async def test_up_then_down(self):
  function test_default_run (line 81) | async def test_default_run(strategy_tester):
  function test_slow_downtrend (line 85) | async def test_slow_downtrend(strategy_tester):
  function test_sharp_downtrend (line 89) | async def test_sharp_downtrend(strategy_tester):
  function test_flat_markets (line 93) | async def test_flat_markets(strategy_tester):
  function test_slow_uptrend (line 97) | async def test_slow_uptrend(strategy_tester):
  function test_sharp_uptrend (line 101) | async def test_sharp_uptrend(strategy_tester):
  function test_up_then_down (line 105) | async def test_up_then_down(strategy_tester):

FILE: Evaluator/TA/ai_evaluator/ai.py
  function _get_gpt_service (line 33) | def _get_gpt_service():
  class GPTEvaluator (line 40) | class GPTEvaluator(evaluators.TAEvaluator):
    method __init__ (line 62) | def __init__(self, tentacles_setup_config):
    method enable_reevaluation (line 83) | def enable_reevaluation(self) -> bool:
    method get_signals_history_type (line 90) | def get_signals_history_type(cls):
    method load_and_save_user_inputs (line 96) | async def load_and_save_user_inputs(self, bot_id: str) -> dict:
    method init_user_inputs (line 108) | def init_user_inputs(self, inputs: dict) -> None:
    method _init_GPT_models (line 155) | async def _init_GPT_models(self):
    method _init_registered_topics (line 168) | async def _init_registered_topics(self, all_symbols_by_crypto_currenci...
    method ohlcv_callback (line 177) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 182) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...
    method get_formatted_data (line 227) | def get_formatted_data(self, computed_data) -> str:
    method ask_gpt (line 233) | async def ask_gpt(self, preprompt, inputs, symbol, time_frame, candle_...
    method get_version (line 263) | def get_version(self):
    method call_indicator (line 268) | def call_indicator(self, candle_data):
    method get_candles_data (line 273) | def get_candles_data(self, exchange, exchange_id, symbol, time_frame, ...
    method get_unformated_sources (line 292) | def get_unformated_sources(self):
    method get_candles_data_api (line 295) | def get_candles_data_api(self):
    method _check_timeframe (line 304) | def _check_timeframe(self, time_frame):
    method _parse_prediction_side (line 308) | def _parse_prediction_side(self, cleaned_prediction):
    method _parse_confidence (line 315) | def _parse_confidence(self, cleaned_prediction):

FILE: Evaluator/TA/ai_evaluator/tests/test_ai.py
  function GPT_evaluator (line 25) | def GPT_evaluator():
  function test_indicators (line 29) | def test_indicators(GPT_evaluator):
  function test_get_candles_data_api (line 37) | def test_get_candles_data_api(GPT_evaluator):
  function test_parse_prediction_side (line 44) | def test_parse_prediction_side(GPT_evaluator):
  function test_parse_confidence (line 56) | def test_parse_confidence(GPT_evaluator):

FILE: Evaluator/TA/momentum_evaluator/momentum.py
  class RSIMomentumEvaluator (line 30) | class RSIMomentumEvaluator(evaluators.TAEvaluator):
    method __init__ (line 36) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 46) | def init_user_inputs(self, inputs: dict) -> None:
    method get_default_config (line 85) | def get_default_config(
    method ohlcv_callback (line 96) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 103) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...
    method get_is_symbol_wildcard (line 141) | def get_is_symbol_wildcard(cls) -> bool:
    method get_is_time_frame_wildcard (line 148) | def get_is_time_frame_wildcard(cls) -> bool:
  class RSIWeightMomentumEvaluator (line 156) | class RSIWeightMomentumEvaluator(evaluators.TAEvaluator):
    method get_eval_type (line 169) | def get_eval_type():
    method __init__ (line 172) | def __init__(self, tentacles_setup_config):
    method _init_fast_threshold (line 179) | def _init_fast_threshold(self, inputs, indexes, fast_threshold, price_...
    method _init_RSI_to_weight (line 200) | def _init_RSI_to_weight(self, inputs, slow_threshold, fast_thresholds):
    method init_user_inputs (line 218) | def init_user_inputs(self, inputs: dict) -> None:
    method _get_rsi_averages (line 249) | def _get_rsi_averages(self, symbol_candles, time_frame, include_in_con...
    method _check_inferior (line 263) | def _check_inferior(bound, val1, val2):
    method _analyse_dip_weight (line 266) | def _analyse_dip_weight(self, slow_rsi, fast_rsi, current_rsi):
    method ohlcv_callback (line 281) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 296) | async def evaluate(self, cryptocurrency, symbol, time_frame, slow_rsi,
  class BBMomentumEvaluator (line 318) | class BBMomentumEvaluator(evaluators.TAEvaluator):
    method __init__ (line 320) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 324) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 329) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 337) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...
  class EMAMomentumEvaluator (line 386) | class EMAMomentumEvaluator(evaluators.TAEvaluator):
    method __init__ (line 391) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 398) | def init_user_inputs(self, inputs: dict) -> None:
    method get_default_config (line 421) | def get_default_config(
    method ohlcv_callback (line 432) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 440) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...
  class ADXMomentumEvaluator (line 459) | class ADXMomentumEvaluator(evaluators.TAEvaluator):
    method __init__ (line 461) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 465) | def init_user_inputs(self, inputs: dict) -> None:
    method _get_minimal_data (line 470) | def _get_minimal_data(self):
    method ohlcv_callback (line 477) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 494) | async def evaluate(self, cryptocurrency, symbol, time_frame, close_can...
  class MACDMomentumEvaluator (line 542) | class MACDMomentumEvaluator(evaluators.TAEvaluator):
    method __init__ (line 543) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 550) | def init_user_inputs(self, inputs: dict) -> None:
    method _analyse_pattern (line 564) | def _analyse_pattern(self, pattern, macd_hist, zero_crossing_indexes, ...
    method ohlcv_callback (line 599) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 606) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...
  class KlingerOscillatorMomentumEvaluator (line 642) | class KlingerOscillatorMomentumEvaluator(evaluators.TAEvaluator):
    method __init__ (line 643) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 649) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 662) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 682) | async def evaluate(self, cryptocurrency, symbol, time_frame, high_cand...
  class KlingerOscillatorReversalConfirmationMomentumEvaluator (line 722) | class KlingerOscillatorReversalConfirmationMomentumEvaluator(evaluators....
    method __init__ (line 723) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 729) | def init_user_inputs(self, inputs: dict) -> None:
    method get_eval_type (line 747) | def get_eval_type():
    method ohlcv_callback (line 750) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 770) | async def evaluate(self, cryptocurrency, symbol, time_frame, high_cand...

FILE: Evaluator/TA/momentum_evaluator/tests/test_adx_momentum_evaluator.py
  function evaluator_tester (line 29) | async def evaluator_tester():
  class TestADXTAEvaluator (line 35) | class TestADXTAEvaluator(abstract_TA_test.AbstractTATest):
    method test_stress_test (line 38) | async def test_stress_test(evaluator_tester):
    method test_reactions_to_dump (line 42) | async def test_reactions_to_dump(evaluator_tester):
    method test_reactions_to_pump (line 46) | async def test_reactions_to_pump(evaluator_tester):
    method test_reaction_to_rise_after_over_sold (line 50) | async def test_reaction_to_rise_after_over_sold(evaluator_tester):
    method test_reaction_to_over_bought_then_dip (line 54) | async def test_reaction_to_over_bought_then_dip(evaluator_tester):
    method test_reaction_to_flat_trend (line 58) | async def test_reaction_to_flat_trend(evaluator_tester):

FILE: Evaluator/TA/momentum_evaluator/tests/test_bollinger_bands_momentum_TA_evaluator.py
  function evaluator_tester (line 30) | async def evaluator_tester():
  class TestBollingerBandsMomentumeEvaluator (line 36) | class TestBollingerBandsMomentumeEvaluator(abstract_TA_test.AbstractTATe...
    method test_stress_test (line 39) | async def test_stress_test(evaluator_tester):
    method test_reactions_to_dump (line 43) | async def test_reactions_to_dump(evaluator_tester):
    method test_reactions_to_pump (line 47) | async def test_reactions_to_pump(evaluator_tester):
    method test_reaction_to_rise_after_over_sold (line 51) | async def test_reaction_to_rise_after_over_sold(evaluator_tester):
    method test_reaction_to_over_bought_then_dip (line 55) | async def test_reaction_to_over_bought_then_dip(evaluator_tester):
    method test_reaction_to_flat_trend (line 59) | async def test_reaction_to_flat_trend(evaluator_tester):

FILE: Evaluator/TA/momentum_evaluator/tests/test_klinger_TA_evaluator.py
  function evaluator_tester (line 30) | async def evaluator_tester():
  class TestKlingerEvaluator (line 36) | class TestKlingerEvaluator(abstract_TA_test.AbstractTATest):
    method test_stress_test (line 39) | async def test_stress_test(evaluator_tester):
    method test_reactions_to_dump (line 43) | async def test_reactions_to_dump(evaluator_tester):
    method test_reactions_to_pump (line 47) | async def test_reactions_to_pump(evaluator_tester):
    method test_reaction_to_rise_after_over_sold (line 52) | async def test_reaction_to_rise_after_over_sold(evaluator_tester):
    method test_reaction_to_over_bought_then_dip (line 56) | async def test_reaction_to_over_bought_then_dip(evaluator_tester):
    method test_reaction_to_flat_trend (line 60) | async def test_reaction_to_flat_trend(evaluator_tester):

FILE: Evaluator/TA/momentum_evaluator/tests/test_macd_TA_evaluator.py
  function evaluator_tester (line 30) | async def evaluator_tester():
  class TestMACDEvaluator (line 36) | class TestMACDEvaluator(abstract_TA_test.AbstractTATest):
    method test_stress_test (line 39) | async def test_stress_test(evaluator_tester):
    method test_reactions_to_dump (line 43) | async def test_reactions_to_dump(evaluator_tester):
    method test_reactions_to_pump (line 47) | async def test_reactions_to_pump(evaluator_tester):
    method test_reaction_to_rise_after_over_sold (line 51) | async def test_reaction_to_rise_after_over_sold(evaluator_tester):
    method test_reaction_to_over_bought_then_dip (line 55) | async def test_reaction_to_over_bought_then_dip(evaluator_tester):
    method test_reaction_to_flat_trend (line 59) | async def test_reaction_to_flat_trend(evaluator_tester):

FILE: Evaluator/TA/momentum_evaluator/tests/test_rsi_TA_evaluator.py
  function evaluator_tester (line 29) | async def evaluator_tester():
  class TestRSIEvaluator (line 35) | class TestRSIEvaluator(abstract_TA_test.AbstractTATest):
    method test_stress_test (line 38) | async def test_stress_test(evaluator_tester):
    method test_reactions_to_dump (line 42) | async def test_reactions_to_dump(evaluator_tester):
    method test_reactions_to_pump (line 46) | async def test_reactions_to_pump(evaluator_tester):
    method test_reaction_to_rise_after_over_sold (line 50) | async def test_reaction_to_rise_after_over_sold(evaluator_tester):
    method test_reaction_to_over_bought_then_dip (line 54) | async def test_reaction_to_over_bought_then_dip(evaluator_tester):
    method test_reaction_to_flat_trend (line 58) | async def test_reaction_to_flat_trend(evaluator_tester):

FILE: Evaluator/TA/trend_evaluator/tests/test_double_moving_averages_TA_evaluator.py
  function evaluator_tester (line 30) | async def evaluator_tester():
  class TestDoubleMovingAveragesEvaluator (line 36) | class TestDoubleMovingAveragesEvaluator(AbstractTATest):
    method test_stress_test (line 39) | async def test_stress_test(evaluator_tester):
    method test_reactions_to_dump (line 43) | async def test_reactions_to_dump(evaluator_tester):
    method test_reactions_to_pump (line 47) | async def test_reactions_to_pump(evaluator_tester):
    method test_reaction_to_rise_after_over_sold (line 51) | async def test_reaction_to_rise_after_over_sold(evaluator_tester):
    method test_reaction_to_over_bought_then_dip (line 55) | async def test_reaction_to_over_bought_then_dip(evaluator_tester):
    method test_reaction_to_flat_trend (line 59) | async def test_reaction_to_flat_trend(evaluator_tester):

FILE: Evaluator/TA/trend_evaluator/trend.py
  class SuperTrendEvaluator (line 30) | class SuperTrendEvaluator(evaluators.TAEvaluator):
    method __init__ (line 38) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 46) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 59) | async def ohlcv_callback(self, exchange: str, exchange_id: str, crypto...
    method evaluate (line 75) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle, h...
    method get_previous_value (line 130) | def get_previous_value(self, symbol, time_frame):
  class DeathAndGoldenCrossEvaluator (line 143) | class DeathAndGoldenCrossEvaluator(evaluators.TAEvaluator):
    method __init__ (line 150) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 158) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 171) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 187) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle, c...
  class DoubleMovingAverageTrendEvaluator (line 223) | class DoubleMovingAverageTrendEvaluator(evaluators.TAEvaluator):
    method __init__ (line 225) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 230) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 241) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 248) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...
    method get_moving_average_analysis (line 268) | def get_moving_average_analysis(data, current_moving_average, time_per...
  class EMADivergenceTrendEvaluator (line 307) | class EMADivergenceTrendEvaluator(evaluators.TAEvaluator):
    method __init__ (line 312) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 318) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 331) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 338) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...

FILE: Evaluator/TA/volatility_evaluator/volatility.py
  class StochasticRSIVolatilityEvaluator (line 26) | class StochasticRSIVolatilityEvaluator(evaluators.TAEvaluator):
    method __init__ (line 32) | def __init__(self, tentacles_setup_config):
    method init_user_inputs (line 38) | def init_user_inputs(self, inputs: dict) -> None:
    method ohlcv_callback (line 51) | async def ohlcv_callback(self, exchange: str, exchange_id: str,
    method evaluate (line 58) | async def evaluate(self, cryptocurrency, symbol, time_frame, candle_da...

FILE: Evaluator/Util/candles_util/candles_util.py
  class CandlesUtil (line 20) | class CandlesUtil:
    method HL2 (line 23) | def HL2(candles_high, candles_low):
    method HLC3 (line 34) | def HLC3(candles_high, candles_low, candles_close):
    method OHLC4 (line 47) | def OHLC4(candles_open, candles_high, candles_low, candles_close):
    method HeikinAshi (line 61) | def HeikinAshi(candles_open, candles_high, candles_low, candles_close):

FILE: Evaluator/Util/candles_util/tests/test_candles_util.py
  function test_HL2 (line 22) | def test_HL2():
  function test_HLC3 (line 34) | def test_HLC3():
  function test_OHLC4 (line 49) | def test_OHLC4():
  function test_HeikinAshi (line 66) | def test_HeikinAshi():

FILE: Evaluator/Util/overall_state_analysis/overall_state_analysis.py
  class OverallStateAnalyser (line 22) | class OverallStateAnalyser:
    method __init__ (line 23) | def __init__(self):
    method add_evaluation (line 30) | def add_evaluation(self, evaluation, weight, refresh_overall_state=True):
    method get_overall_state_after_refresh (line 35) | def get_overall_state_after_refresh(self, refresh_overall_state=True):
    method _refresh_overall_state (line 41) | def _refresh_overall_state(self):
  class StateEvaluation (line 48) | class StateEvaluation:
    method __init__ (line 49) | def __init__(self, value, weight):

FILE: Evaluator/Util/pattern_analysis/pattern_analysis.py
  class PatternAnalyser (line 21) | class PatternAnalyser:
    method find_pattern (line 30) | def find_pattern(data, zero_crossing_indexes, data_frame_max_index):
    method get_pattern (line 65) | def get_pattern(data):
    method get_pattern_strength (line 88) | def get_pattern_strength(pattern):

FILE: Evaluator/Util/statistics_analysis/statistics_analysis.py
  class StatisticAnalysis (line 22) | class StatisticAnalysis:
    method analyse_recent_trend_changes (line 27) | def analyse_recent_trend_changes(data, delta_function):

FILE: Evaluator/Util/text_analysis/text_analysis.py
  class VaderSentimentImportMock (line 23) | class VaderSentimentImportMock:
    class SentimentIntensityAnalyzer (line 24) | class SentimentIntensityAnalyzer:
      method __init__ (line 25) | def __init__(self, *args):
  class TextAnalysis (line 30) | class TextAnalysis:
    method __init__ (line 35) | def __init__(self):
    method analyse (line 40) | def analyse(self,  text):
    method get_high_value_websites (line 48) | def get_high_value_websites():
    method is_analysable_url (line 54) | def is_analysable_url(url):
    method test (line 59) | def test(self):

FILE: Evaluator/Util/trend_analysis/trend_analysis.py
  class TrendAnalysis (line 20) | class TrendAnalysis:
    method get_trend (line 25) | def get_trend(data, averages_to_use):
    method peak_has_been_reached_already (line 47) | def peak_has_been_reached_already(data, neutral_val=0):
    method min_has_just_been_reached (line 60) | def min_has_just_been_reached(data, acceptance_window=0.8, delay=1):
    method detect_divergence (line 71) | def detect_divergence(data_frame, indicator_data_frame):
    method get_estimation_of_move_state_relatively_to_previous_moves_length (line 84) | def get_estimation_of_move_state_relatively_to_previous_moves_length(m...
    method get_threshold_change_indexes (line 112) | def get_threshold_change_indexes(data, threshold):
    method have_just_crossed_over (line 143) | def have_just_crossed_over(list_1, list_2):

FILE: Meta/DSL_operators/exchange_operators/exchange_operator.py
  class ExchangeOperator (line 27) | class ExchangeOperator(dsl_interpreter_call_operator.CallOperator):
    method get_library (line 30) | def get_library() -> str:
    method get_context (line 36) | async def get_context(

FILE: Meta/DSL_operators/exchange_operators/exchange_private_data_operators/portfolio_operators.py
  class PortfolioOperator (line 29) | class PortfolioOperator(exchange_operator.ExchangeOperator):
    method __init__ (line 30) | def __init__(self, *parameters: dsl_interpreter.OperatorParameterType,...
    method get_library (line 35) | def get_library() -> str:
    method get_parameters (line 40) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 45) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  function create_portfolio_operators (line 51) | def create_portfolio_operators(

FILE: Meta/DSL_operators/exchange_operators/exchange_public_data_operators/ohlcv_operators.py
  class ExchangeDataDependency (line 35) | class ExchangeDataDependency(dsl_interpreter.InterpreterDependency):
    method __hash__ (line 41) | def __hash__(self) -> int:
  class OHLCVOperator (line 45) | class OHLCVOperator(exchange_operator.ExchangeOperator):
    method __init__ (line 46) | def __init__(self, *parameters: dsl_interpreter.OperatorParameterType,...
    method get_library (line 51) | def get_library() -> str:
    method get_parameters (line 56) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method get_symbol_and_time_frame (line 62) | def get_symbol_and_time_frame(self) -> typing.Tuple[typing.Optional[st...
    method compute (line 72) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  function create_ohlcv_operators (line 78) | def create_ohlcv_operators(
  function _get_kline (line 209) | def _get_kline(
  function _get_candles_values (line 218) | def _get_candles_values(
  function _adapt_last_candle_value (line 238) | def _adapt_last_candle_value(

FILE: Meta/DSL_operators/exchange_operators/tests/__init__.py
  function historical_prices (line 37) | def historical_prices():
  function historical_times (line 45) | def historical_times(historical_prices):
  function historical_volume (line 52) | def historical_volume(historical_prices):
  function _get_candle_managers (line 60) | def _get_candle_managers(historical_prices, historical_volume, historica...
  function _get_kline (line 113) | def _get_kline(candles_manager: mock.Mock, signature: float, kline_time_...
  function _get_symbol_data_factory (line 128) | def _get_symbol_data_factory(
  function exchange_manager_with_candles (line 170) | def exchange_manager_with_candles(historical_prices, historical_volume, ...
  function exchange_manager_with_candles_and_klines (line 186) | def exchange_manager_with_candles_and_klines(historical_prices, historic...
  function exchange_manager_with_candles_and_new_candle_klines (line 202) | def exchange_manager_with_candles_and_new_candle_klines(historical_price...
  function candle_manager_by_time_frame_by_symbol (line 218) | def candle_manager_by_time_frame_by_symbol(historical_prices, historical...
  function interpreter (line 234) | def interpreter(exchange_manager_with_candles):
  function interpreter_with_exchange_manager_and_klines (line 242) | def interpreter_with_exchange_manager_and_klines(exchange_manager_with_c...
  function interpreter_with_exchange_manager_and_new_candle_klines (line 250) | def interpreter_with_exchange_manager_and_new_candle_klines(exchange_man...
  function interpreter_with_candle_manager_by_time_frame_by_symbol (line 258) | def interpreter_with_candle_manager_by_time_frame_by_symbol(candle_manag...

FILE: Meta/DSL_operators/exchange_operators/tests/exchange_public_data_operators/test_ohlcv_operators.py
  function expected_values (line 50) | def expected_values(request, historical_prices, historical_volume, histo...
  function operator (line 62) | def operator(request):
  function test_ohlcv_operators_basic_calls_without_klines (line 75) | async def test_ohlcv_operators_basic_calls_without_klines(
  function _adapted_for_kline (line 100) | def _adapted_for_kline(values: np.ndarray, operator: str, time_delay: fl...
  function test_ohlcv_operators_basic_calls_with_klines (line 118) | async def test_ohlcv_operators_basic_calls_with_klines(
  function test_ohlcv_operators_basic_calls_with_new_candle_klines (line 152) | async def test_ohlcv_operators_basic_calls_with_new_candle_klines(
  function test_ohlcv_operators_dependencies (line 210) | async def test_ohlcv_operators_dependencies(interpreter, operator, excha...

FILE: Meta/DSL_operators/exchange_operators/tests/test_mocks.py
  function test_interpreter_mock (line 41) | async def test_interpreter_mock(interpreter, historical_prices, historic...
  function test_interpreter_with_exchange_manager_and_klines_mock (line 57) | async def test_interpreter_with_exchange_manager_and_klines_mock(
  function test_interpreter_with_exchange_manager_and_new_candle_klines_mock (line 80) | async def test_interpreter_with_exchange_manager_and_new_candle_klines_m...
  function test_interpreter_with_candle_manager_by_time_frame_by_symbol_mock (line 104) | async def test_interpreter_with_candle_manager_by_time_frame_by_symbol_m...

FILE: Meta/DSL_operators/python_std_operators/base_binary_operators.py
  class AddOperator (line 23) | class AddOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 29) | def get_name() -> str:
    method compute (line 32) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class SubOperator (line 37) | class SubOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 43) | def get_name() -> str:
    method compute (line 46) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class MultOperator (line 51) | class MultOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 57) | def get_name() -> str:
    method compute (line 60) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class DivOperator (line 65) | class DivOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 71) | def get_name() -> str:
    method compute (line 74) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class FloorDivOperator (line 79) | class FloorDivOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 85) | def get_name() -> str:
    method compute (line 88) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class ModOperator (line 93) | class ModOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 99) | def get_name() -> str:
    method compute (line 102) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class PowOperator (line 107) | class PowOperator(dsl_interpreter_binary_operator.BinaryOperator):
    method get_name (line 113) | def get_name() -> str:
    method compute (line 116) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/base_call_operators.py
  class MinOperator (line 23) | class MinOperator(dsl_interpreter.CallOperator):
    method get_name (line 30) | def get_name() -> str:
    method compute (line 33) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class MaxOperator (line 38) | class MaxOperator(dsl_interpreter.CallOperator):
    method get_name (line 45) | def get_name() -> str:
    method compute (line 48) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class MeanOperator (line 53) | class MeanOperator(dsl_interpreter.CallOperator):
    method get_name (line 60) | def get_name() -> str:
    method compute (line 63) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class SqrtOperator (line 77) | class SqrtOperator(dsl_interpreter.CallOperator):
    method get_name (line 85) | def get_name() -> str:
    method compute (line 88) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class AbsOperator (line 98) | class AbsOperator(dsl_interpreter.CallOperator):
    method get_name (line 106) | def get_name() -> str:
    method compute (line 109) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class RoundOperator (line 115) | class RoundOperator(dsl_interpreter.CallOperator):
    method get_name (line 121) | def get_name() -> str:
    method get_parameters (line 125) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 131) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class FloorOperator (line 142) | class FloorOperator(dsl_interpreter.CallOperator):
    method get_name (line 150) | def get_name() -> str:
    method compute (line 153) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class CeilOperator (line 163) | class CeilOperator(dsl_interpreter.CallOperator):
    method get_name (line 171) | def get_name() -> str:
    method compute (line 174) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:

FILE: Meta/DSL_operators/python_std_operators/base_compare_operators.py
  class EqOperator (line 23) | class EqOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 29) | def get_name() -> str:
    method compute (line 32) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class NotEqOperator (line 37) | class NotEqOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 43) | def get_name() -> str:
    method compute (line 46) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class LtOperator (line 51) | class LtOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 57) | def get_name() -> str:
    method compute (line 60) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class LtEOperator (line 65) | class LtEOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 71) | def get_name() -> str:
    method compute (line 74) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class GtOperator (line 79) | class GtOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 85) | def get_name() -> str:
    method compute (line 88) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class GtEOperator (line 93) | class GtEOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 99) | def get_name() -> str:
    method compute (line 102) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class IsOperator (line 107) | class IsOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 113) | def get_name() -> str:
    method compute (line 116) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class IsNotOperator (line 121) | class IsNotOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 127) | def get_name() -> str:
    method compute (line 130) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class InOperator (line 135) | class InOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 141) | def get_name() -> str:
    method compute (line 144) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class NotInOperator (line 149) | class NotInOperator(dsl_interpreter_compare_operator.CompareOperator):
    method get_name (line 155) | def get_name() -> str:
    method compute (line 158) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/base_expression_operators.py
  class IfExpOperator (line 23) | class IfExpOperator(dsl_interpreter_expression_operator.ExpressionOperat...
    method __init__ (line 32) | def __init__(
    method get_name (line 44) | def get_name() -> str:
    method compute (line 47) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/base_iterable_operators.py
  class ListOperator (line 23) | class ListOperator(dsl_interpreter_iterable_operator.IterableOperator):
    method get_name (line 33) | def get_name() -> str:
    method compute (line 36) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/base_name_operators.py
  class PiOperator (line 23) | class PiOperator(dsl_interpreter_name_operator.NameOperator):
    method get_name (line 30) | def get_name() -> str:
    method compute (line 33) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class NaNOperator (line 37) | class NaNOperator(dsl_interpreter_name_operator.NameOperator):
    method get_name (line 44) | def get_name() -> str:
    method compute (line 47) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/base_nary_operators.py
  class AndOperator (line 23) | class AndOperator(dsl_interpreter_n_ary_operator.NaryOperator):
    method get_name (line 31) | def get_name() -> str:
    method compute (line 34) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class OrOperator (line 39) | class OrOperator(dsl_interpreter_n_ary_operator.NaryOperator):
    method get_name (line 47) | def get_name() -> str:
    method compute (line 50) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/base_subscripting_operators.py
  class SubscriptOperator (line 26) | class SubscriptOperator(dsl_interpreter_subscripting_operator.Subscripti...
    method __init__ (line 35) | def __init__(
    method get_computed_array_or_list_and_index_or_slice_and_context_parameters (line 47) | def get_computed_array_or_list_and_index_or_slice_and_context_parameters(
    method get_name (line 65) | def get_name() -> str:
    method compute (line 68) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class SliceOperator (line 76) | class SliceOperator(dsl_interpreter_subscripting_operator.SubscriptingOp...
    method get_name (line 86) | def get_name() -> str:
    method get_computed_lower_and_upper_and_step_parameters (line 89) | def get_computed_lower_and_upper_and_step_parameters(
    method compute (line 107) | def compute(self) -> slice:

FILE: Meta/DSL_operators/python_std_operators/base_unary_operators.py
  class UAddOperator (line 23) | class UAddOperator(dsl_interpreter_unary_operator.UnaryOperator):
    method get_name (line 29) | def get_name() -> str:
    method compute (line 32) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class USubOperator (line 37) | class USubOperator(dsl_interpreter_unary_operator.UnaryOperator):
    method get_name (line 43) | def get_name() -> str:
    method compute (line 46) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class NotOperator (line 51) | class NotOperator(dsl_interpreter_unary_operator.UnaryOperator):
    method get_name (line 57) | def get_name() -> str:
    method compute (line 60) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...
  class InvertOperator (line 65) | class InvertOperator(dsl_interpreter_unary_operator.UnaryOperator):
    method get_name (line 71) | def get_name() -> str:
    method compute (line 74) | def compute(self) -> dsl_interpreter_operator.ComputedOperatorParamete...

FILE: Meta/DSL_operators/python_std_operators/tests/test_base_operators.py
  function interpreter (line 23) | def interpreter():
  function test_interpreter_basic_operations (line 28) | async def test_interpreter_basic_operations(interpreter):
  function test_interpreter_mixed_basic_operations (line 95) | async def test_interpreter_mixed_basic_operations(interpreter):
  function test_interpreter_call_operations (line 114) | async def test_interpreter_call_operations(interpreter):
  function test_interpreter_mixed_call_and_basic_operations (line 131) | async def test_interpreter_mixed_call_and_basic_operations(interpreter):
  function test_interpreter_insupported_operations (line 144) | async def test_interpreter_insupported_operations(interpreter):

FILE: Meta/DSL_operators/python_std_operators/tests/test_dictionnaries.py
  function test_get_all_operators (line 26) | def test_get_all_operators(libraries):

FILE: Meta/DSL_operators/ta_operators/ta_operator.py
  class TAOperator (line 23) | class TAOperator(dsl_interpreter_call_operator.CallOperator):
    method get_library (line 26) | def get_library() -> str:

FILE: Meta/DSL_operators/ta_operators/tests/test_docs_examples.py
  function test_mm_formulas_docs_examples (line 28) | async def test_mm_formulas_docs_examples(interpreter):

FILE: Meta/DSL_operators/ta_operators/tests/test_tulipy_technical_analysis_operators.py
  function test_operator_invalid_static_parameters (line 37) | async def test_operator_invalid_static_parameters(interpreter, operator,...
  function test_operator_invalid_dynamic_parameters (line 56) | async def test_operator_invalid_dynamic_parameters(interpreter, operator...
  function test_operator_converted_tulipy_error (line 74) | async def test_operator_converted_tulipy_error(interpreter, operator, dy...
  function test_operator_operations (line 84) | async def test_operator_operations(interpreter):
  function test_rsi_operator (line 101) | async def test_rsi_operator(interpreter):
  function test_macd_operator (line 121) | async def test_macd_operator(interpreter):
  function test_ma_operator (line 147) | async def test_ma_operator(interpreter):
  function test_vwma_operator (line 165) | async def test_vwma_operator(interpreter):
  function test_ema_operator (line 183) | async def test_ema_operator(interpreter):

FILE: Meta/DSL_operators/ta_operators/tulipy_technical_analysis_operators.py
  function _to_numpy_array (line 25) | def _to_numpy_array(data):
  function _to_int (line 38) | def _to_int(value):
  function converted_tulipy_error (line 47) | def converted_tulipy_error(f):
  class RSIOperator (line 58) | class RSIOperator(ta_operator.TAOperator):
    method get_name (line 63) | def get_name() -> str:
    method get_parameters (line 67) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 74) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class MACDOperator (line 79) | class MACDOperator(ta_operator.TAOperator):
    method get_name (line 84) | def get_name() -> str:
    method get_parameters (line 88) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 97) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class MAOperator (line 105) | class MAOperator(ta_operator.TAOperator):
    method get_name (line 110) | def get_name() -> str:
    method get_parameters (line 114) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 121) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class EMAOperator (line 126) | class EMAOperator(ta_operator.TAOperator):
    method get_name (line 131) | def get_name() -> str:
    method get_parameters (line 135) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 142) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:
  class VWMAOperator (line 147) | class VWMAOperator(ta_operator.TAOperator):
    method get_name (line 152) | def get_name() -> str:
    method get_parameters (line 156) | def get_parameters() -> list[dsl_interpreter.OperatorParameter]:
    method compute (line 164) | def compute(self) -> dsl_interpreter.ComputedOperatorParameterType:

FILE: Meta/Keywords/scripting_library/TA/trigger/eval_triggered.py
  function _is_first_candle_only (line 32) | def _is_first_candle_only(context):
  function _is_first_candle_call (line 40) | def _is_first_candle_call(context, init_key):
  function evaluator_get_result (line 45) | async def evaluator_get_result(
  function evaluator_get_results (line 90) | async def evaluator_get_results(
  function _ensure_cache_when_set_value_key (line 141) | def _ensure_cache_when_set_value_key(value_key, tentacle_class):
  function _trigger_single_evaluation (line 148) | async def _trigger_single_evaluation(context, tentacle_class, value_key,...
  function _init_nested_call (line 199) | async def _init_nested_call(context, tentacle_class, config_name, cleane...
  function _get_init_key (line 215) | def _get_init_key(context, config_name):
  function _invalidate_call_and_parents_init_status (line 219) | def _invalidate_call_and_parents_init_status(context, config_name):
  function _tentacle_values (line 227) | def _tentacle_values(context,

FILE: Meta/Keywords/scripting_library/UI/inputs/library_user_inputs.py
  function _find_configuration (line 23) | def _find_configuration(nested_configuration, nested_config_names, eleme...
  function external_user_input (line 34) | async def external_user_input(

FILE: Meta/Keywords/scripting_library/UI/inputs/select_candle.py
  function user_select_candle (line 20) | async def user_select_candle(

FILE: Meta/Keywords/scripting_library/UI/inputs/select_history.py
  function set_candles_history_size (line 21) | async def set_candles_history_size(

FILE: Meta/Keywords/scripting_library/UI/inputs/select_time_frame.py
  function user_select_time_frame (line 25) | async def user_select_time_frame(
  function user_multi_select_time_frame (line 40) | async def user_multi_select_time_frame(
  function set_trigger_time_frames (line 55) | async def set_trigger_time_frames(

FILE: Meta/Keywords/scripting_library/UI/inputs/triggers.py
  function trigger_only_on_the_first_candle (line 22) | async def trigger_only_on_the_first_candle(ctx,

FILE: Meta/Keywords/scripting_library/UI/plots/displayed_elements.py
  class DisplayedElements (line 28) | class DisplayedElements(display.DisplayTranslator):
    method fill_from_database (line 41) | async def fill_from_database(self, trading_mode, database_manager, exc...
    method _adapt_inputs_for_backtesting_results (line 110) | async def _adapt_inputs_for_backtesting_results(self, meta_db, exchang...
    method _plot_graphs (line 138) | def _plot_graphs(self, graphs_by_parts):
    method _adapt_for_display (line 226) | def _adapt_for_display(self, table_name, filtered_elements):
    method _filter_and_adapt_displayed_elements (line 264) | def _filter_and_adapt_displayed_elements(self, elements, symbol, time_...
    method _get_run_window (line 279) | async def _get_run_window(self, run_database):
    method _add_cached_values (line 287) | async def _add_cached_values(self, graphs_by_parts, cached_values, tim...
    method _get_cached_values_to_display (line 312) | async def _get_cached_values_to_display(self, cached_value_metadata, x...
    method _get_cache_displayed_value (line 372) | def _get_cache_displayed_value(cache_val, base_displayed_value):
    method _add_candles (line 379) | async def _add_candles(self, graphs_by_parts, candles_list, exchange_n...
    method _get_candles_to_display (line 403) | async def _get_candles_to_display(self, candles_metadata, exchange_nam...
    method plot (line 445) | def plot(
    method table (line 491) | def table(
    method value (line 510) | def value(self, label, value):
    method html_value (line 521) | def html_value(self, html):

FILE: Meta/Keywords/scripting_library/alerts/notifications.py
  function send_alert (line 5) | async def send_alert(title, alert_content,

FILE: Meta/Keywords/scripting_library/backtesting/backtesting_data_collector.py
  function init_exchange_market_status_and_populate_backtesting_exchange_data (line 45) | async def init_exchange_market_status_and_populate_backtesting_exchange_...
  function fetch_and_populate_backtesting_exchange_data (line 70) | async def fetch_and_populate_backtesting_exchange_data(
  function _get_backtesting_run_details (line 90) | def _get_backtesting_run_details(
  function get_backtesting_start_time (line 108) | def get_backtesting_start_time(
  function iter_fetched_ohlcvs (line 114) | def iter_fetched_ohlcvs(ohlcvs: list[list[typing.Union[float, str]]]):
  function populate_backtesting_exchange_data_from_historical_client (line 130) | async def populate_backtesting_exchange_data_from_historical_client(
  function init_backtesting_exchange_market_status_cache (line 170) | async def init_backtesting_exchange_market_status_cache(
  function data_collector_ccxt_exchange_manager (line 180) | async def data_collector_ccxt_exchange_manager(
  function fetch_candles_history_range (line 198) | async def fetch_candles_history_range(
  function find_usd_like_symbol_from_available_history (line 205) | async def find_usd_like_symbol_from_available_history(
  function update_backtesting_symbols_data (line 236) | async def update_backtesting_symbols_data(
  function _ensure_start_time (line 296) | def _ensure_start_time(
  function ensure_ohlcv_validity (line 311) | def ensure_ohlcv_validity(
  function adapt_exchange_data_for_updated_start_time (line 328) | def adapt_exchange_data_for_updated_start_time(
  function ensure_compatible_candle_time (line 345) | def ensure_compatible_candle_time(
  function _get_logger (line 416) | def _get_logger():

FILE: Meta/Keywords/scripting_library/backtesting/backtesting_data_selector.py
  function backtesting_start_time (line 22) | def backtesting_start_time(ctx):
  function backtesting_first_full_candle_time (line 26) | def backtesting_first_full_candle_time(ctx):
  function backtesting_is_first_full_candle (line 30) | async def backtesting_is_first_full_candle(ctx):
  function backtesting_end_time (line 36) | def backtesting_end_time(ctx):
  function backtesting_last_full_candle_time (line 40) | def backtesting_last_full_candle_time(ctx):
  function _align_time_to_time_frame (line 44) | def _align_time_to_time_frame(reference_time, time_frame, align_backwards):

FILE: Meta/Keywords/scripting_library/backtesting/backtesting_intialization.py
  function init_and_run_backtesting (line 42) | async def init_and_run_backtesting(
  function run_backtesting (line 62) | async def run_backtesting(
  function _init_independent_backtesting (line 90) | def _init_independent_backtesting(
  function _init_backtest_data (line 129) | async def _init_backtest_data(
  function _init_importers (line 145) | async def _init_importers(
  function _init_preloaded_candle_managers (line 156) | async def _init_preloaded_candle_managers(

FILE: Meta/Keywords/scripting_library/backtesting/backtesting_settings.py
  function set_backtesting_iteration_timeout (line 19) | def set_backtesting_iteration_timeout(ctx, iteration_timeout_in_seconds:...
  function register_backtesting_timestamp_whitelist (line 27) | def register_backtesting_timestamp_whitelist(ctx, timestamps, check_call...
  function is_registered_backtesting_timestamp_whitelist (line 50) | def is_registered_backtesting_timestamp_whitelist(ctx):

FILE: Meta/Keywords/scripting_library/backtesting/default_backtesting_run_analysis_script.py
  function default_backtesting_analysis_script (line 11) | async def default_backtesting_analysis_script(ctx: script_keywords.Conte...
  function get_backtesting_report_template (line 86) | async def get_backtesting_report_template(run_data, backtesting_analysis...
  function get_section_display (line 188) | def get_section_display(title, content):
  function get_column_display (line 199) | def get_column_display(title, value):
  function get_badges_from_list (line 215) | def get_badges_from_list(_list):
  function get_portfolio_display (line 222) | def get_portfolio_display(_dict):
  function get_user_inputs_display (line 231) | def get_user_inputs_display(metadata):

FILE: Meta/Keywords/scripting_library/backtesting/metadata.py
  function set_script_name (line 22) | def set_script_name(ctx, name):
  function _read_backtesting_metadata (line 26) | async def _read_backtesting_metadata(optimizer_run_dbs_identifier, metad...
  function read_metadata (line 38) | async def read_metadata(runs_to_load_settings, trading_mode, include_opt...
  function _read_bot_recording_metadata (line 77) | async def _read_bot_recording_metadata(run_dbs_identifier, metadata_list):
  function read_bot_recording_runs_metadata (line 87) | async def read_bot_recording_runs_metadata(trading_mode):

FILE: Meta/Keywords/scripting_library/backtesting/run_data_analysis.py
  function get_logger (line 35) | def get_logger():
  function get_candles (line 39) | async def get_candles(candles_sources, exchange, symbol, time_frame, met...
  function get_trades (line 48) | async def get_trades(meta_database, metadata, symbol):
  function get_metadata (line 56) | async def get_metadata(meta_database):
  function get_transactions (line 60) | async def get_transactions(meta_database, transaction_type=None, transac...
  function get_starting_portfolio (line 72) | async def get_starting_portfolio(meta_database) -> dict:
  function load_historical_values (line 78) | async def load_historical_values(meta_database, exchange, with_candles=T...
  function backtesting_data (line 134) | async def backtesting_data(meta_database, data_label):
  function _get_grouped_funding_fees (line 151) | async def _get_grouped_funding_fees(meta_database, group_key):
  function plot_historical_funding_fees (line 164) | async def plot_historical_funding_fees(meta_database, plotted_element, o...
  function _position_factory (line 185) | def _position_factory(symbol, contract_data):
  function _evaluate_portfolio (line 201) | def _evaluate_portfolio(portfolio, price_data, use_start_value):
  function get_portfolio_values (line 223) | async def get_portfolio_values(meta_database, exchange=None, historical_...
  function plot_historical_portfolio_value (line 239) | async def plot_historical_portfolio_value(
  function _read_pnl_from_trades (line 357) | def _read_pnl_from_trades(x_data, pnl_data, cumulative_pnl_data, trades_...
  function _read_pnl_from_transactions (line 422) | def _read_pnl_from_transactions(x_data, pnl_data, cumulative_pnl_data, t...
  function _get_historical_pnl (line 438) | async def _get_historical_pnl(meta_database, plotted_element, include_cu...
  function total_paid_fees (line 497) | async def total_paid_fees(meta_database, all_trades):
  function plot_historical_pnl_value (line 530) | async def plot_historical_pnl_value(meta_database, plotted_element, exch...
  function _plot_table_data (line 538) | def _plot_table_data(data, plotted_element, data_name, additional_key_to...
  function plot_trades (line 563) | async def plot_trades(meta_database, plotted_element, historical_values=...
  function plot_orders (line 597) | async def plot_orders(meta_database, plotted_element, historical_values=...
  function plot_withdrawals (line 630) | async def plot_withdrawals(meta_database, plotted_element):
  function plot_positions (line 649) | async def plot_positions(meta_database, plotted_element):
  function display (line 668) | async def display(plotted_element, label, value):
  function display_html (line 672) | async def display_html(plotted_element, html):
  function plot_table (line 676) | async def plot_table(meta_database, plotted_element, data_source, column...
  function _get_default_column_render (line 726) | def _get_default_column_render():
  function _get_default_types (line 734) | def _get_default_types():
  function _get_default_columns (line 742) | def _get_default_columns(plotted_element, data, column_render, key_to_la...
  function _get_default_rows (line 755) | def _get_default_rows(data, columns):
  function _get_default_searches (line 763) | def _get_default_searches(columns, types):
  function _get_wins_and_losses_from_transactions (line 774) | def _get_wins_and_losses_from_transactions(x_data, wins_and_losses_data,...
  function _get_wins_and_losses_from_trades (line 792) | def _get_wins_and_losses_from_trades(x_data, wins_and_losses_data, trade...
  function plot_historical_wins_and_losses (line 797) | async def plot_historical_wins_and_losses(meta_database, plotted_element...
  function _get_win_rates_from_transactions (line 830) | def _get_win_rates_from_transactions(x_data, win_rates_data, trading_tra...
  function _get_win_rates_from_trades (line 850) | def _get_win_rates_from_trades(x_data, win_rates_data, trades_history, x...
  function plot_historical_win_rates (line 855) | async def plot_historical_win_rates(meta_database, plotted_element, exch...
  function _get_best_case_growth_from_transactions (line 888) | async def _get_best_case_growth_from_transactions(trading_transactions_h...
  function plot_best_case_growth (line 902) | async def plot_best_case_growth(meta_database, plotted_element, exchange...

FILE: Meta/Keywords/scripting_library/configuration/exchanges_configuration.py
  function get_default_reference_market_per_exchange (line 30) | def get_default_reference_market_per_exchange(exchanges: list[str]) -> d...
  function get_default_exchange_reference_market (line 33) | def get_default_exchange_reference_market(exchange: str) -> str:
  function is_exchange_with_different_public_data_after_auth (line 36) | def is_exchange_with_different_public_data_after_auth(exchange: str) -> ...

FILE: Meta/Keywords/scripting_library/configuration/indexes_configuration.py
  function create_index_config_from_tentacles_config (line 33) | def create_index_config_from_tentacles_config(
  function generate_index_config (line 59) | def generate_index_config(
  function generate_index_backtesting_config (line 103) | def generate_index_backtesting_config(
  function _get_index_trading_config (line 115) | def _get_index_trading_config(

FILE: Meta/Keywords/scripting_library/configuration/profile_data_configuration.py
  function minimal_profile_data (line 55) | def minimal_profile_data() -> commons_profiles.ProfileData:
  function empty_config_proxy (line 64) | def empty_config_proxy(*_, **__):
  function create_backtesting_config (line 68) | def create_backtesting_config(
  function get_config (line 80) | def get_config(
  function get_exchange_config (line 116) | def get_exchange_config(
  function create_profile_data_from_tentacles_config_history (line 141) | def create_profile_data_from_tentacles_config_history(
  function register_historical_configs (line 170) | def register_historical_configs(
  function _apply_master_tentacle_config_edits_to_historical_config (line 203) | def _apply_master_tentacle_config_edits_to_historical_config(tentacle: s...
  function get_historical_added_config_trading_pairs (line 210) | def get_historical_added_config_trading_pairs(
  function get_historical_traded_pairs (line 231) | def get_historical_traded_pairs(
  function _get_historical_index_trading_pairs (line 246) | def _get_historical_index_trading_pairs(
  function add_traded_symbols (line 267) | def add_traded_symbols(
  function expand_traded_pairs_into_currencies (line 282) | def expand_traded_pairs_into_currencies(profile_data, pairs: list[str]):
  function filter_out_missing_symbols (line 293) | def filter_out_missing_symbols(profile_data: commons_profiles.ProfileDat...
  function get_readonly_exchange_auth_details (line 305) | def get_readonly_exchange_auth_details(exchange_internal_name: str) -> e...
  function _get_readonly_exchange_credential_from_env (line 315) | def _get_readonly_exchange_credential_from_env(exchange_name, cred_suffi...
  function is_auth_required_exchanges (line 326) | def is_auth_required_exchanges(
  function _get_is_auth_required_exchange (line 348) | def _get_is_auth_required_exchange(
  function _set_portfolio (line 361) | def _set_portfolio(
  function get_formatted_portfolio (line 368) | def get_formatted_portfolio(portfolio: dict):
  function get_config_by_tentacle (line 375) | def get_config_by_tentacle(profile_data: commons_profiles.ProfileData) -...
  function get_full_tentacles_setup_config (line 382) | def get_full_tentacles_setup_config(
  function merge_profile_data (line 409) | def merge_profile_data(
  function apply_leverage_config (line 439) | def apply_leverage_config(profile_data: commons_profiles.ProfileData):
  function apply_leverage_config_to_trading_mode_config_if_necessary (line 445) | def apply_leverage_config_to_trading_mode_config_if_necessary(trading_mo...
  function _get_trading_mode_config (line 449) | def _get_trading_mode_config(profile_data: commons_profiles.ProfileData):
  function get_trading_mode (line 457) | def get_trading_mode(profile_data: commons_profiles.ProfileData) -> typi...
  function get_traded_symbols (line 464) | def get_traded_symbols(
  function get_traded_coins (line 473) | def get_traded_coins(
  function get_time_frames (line 497) | def get_time_frames(
  function _get_default_time_frame (line 506) | def _get_default_time_frame(profile_data: commons_profiles.ProfileData, ...
  function _get_historical_default_time_frame (line 513) | def _get_historical_default_time_frame(profile_data: commons_profiles.Pr...
  function requires_price_update_timeframe (line 520) | def requires_price_update_timeframe(profile_data: commons_profiles.Profi...
  function get_default_historical_time_frame (line 528) | def get_default_historical_time_frame(profile_data: commons_profiles.Pro...
  function can_convert_ref_market_to_usd_like (line 536) | def can_convert_ref_market_to_usd_like(
  function can_convert_ref_market_to_usd_like_from_symbols (line 545) | def can_convert_ref_market_to_usd_like_from_symbols(
  function set_backtesting_portfolio (line 560) | def set_backtesting_portfolio(profile_data, exchange_data):
  function get_oldest_historical_config_symbols_and_time (line 574) | def get_oldest_historical_config_symbols_and_time(profile_data: commons_...
  function _get_all_tentacles_configured_traded_symbols (line 584) | def _get_all_tentacles_configured_traded_symbols(
  function _get_first_historical_config_time (line 609) | def _get_first_historical_config_time(profile_data: commons_profiles.Pro...
  function get_tentacle_config_traded_symbols (line 628) | def get_tentacle_config_traded_symbols(tentacle: str, config: dict, refe...
  function _get_logger (line 643) | def _get_logger():

FILE: Meta/Keywords/scripting_library/configuration/tentacles_configuration.py
  function get_config_history_propagated_tentacles_config_keys (line 28) | def get_config_history_propagated_tentacles_config_keys(tentacle: str) -...
  function is_trading_mode_tentacle (line 35) | def is_trading_mode_tentacle(tentacle_name: str) -> bool:
  function is_exchange_tentacle (line 42) | def is_exchange_tentacle(tentacle_name: str) -> bool:
  function get_all_exchange_tentacles (line 49) | def get_all_exchange_tentacles() -> list[type[exchanges.RestExchange]]:
  function get_exchange_tentacle_from_name (line 53) | def get_exchange_tentacle_from_name(tentacle_name: str) -> type[exchange...

FILE: Meta/Keywords/scripting_library/data/reading/exchange_private_data/open_positions.py
  function is_current_contract_inverse (line 25) | def is_current_contract_inverse(context, symbol=None, side=trading_enums...
  function open_position_size (line 30) | def open_position_size(
  function is_position_open (line 48) | def is_position_open(
  function is_position_long (line 60) | def is_position_long(
  function is_position_short (line 66) | def is_position_short(

FILE: Meta/Keywords/scripting_library/data/reading/exchange_public_data.py
  function current_live_time (line 31) | def current_live_time(context) -> float:
  function symbol_fees (line 35) | def symbol_fees(context, symbol=None) -> dict:
  function is_futures_trading (line 39) | def is_futures_trading(context) -> bool:
  function _time_frame_to_sec (line 43) | def _time_frame_to_sec(context, time_frame=None):
  function current_candle_time (line 48) | async def current_candle_time(context, symbol=None, time_frame=None, use...
  function current_closed_candle_time (line 60) | async def current_closed_candle_time(context, symbol=None, time_frame=No...
  function Time (line 66) | async def Time(context, symbol=None, time_frame=None, limit=-1, max_hist...
  function current_live_price (line 79) | async def current_live_price(context, symbol=None):
  function current_candle_price (line 85) | async def current_candle_price(context, symbol=None, time_frame=None):
  function Open (line 91) | async def Open(context, symbol=None, time_frame=None, limit=-1, max_hist...
  function High (line 99) | async def High(context, symbol=None, time_frame=None, limit=-1, max_hist...
  function Low (line 107) | async def Low(context, symbol=None, time_frame=None, limit=-1, max_histo...
  function Close (line 115) | async def Close(context, symbol=None, time_frame=None, limit=-1, max_his...
  function hl2 (line 122) | async def hl2(context, symbol=None, time_frame=None, limit=-1, max_histo...
  function hlc3 (line 134) | async def hlc3(context, symbol=None, time_frame=None, limit=-1, max_hist...
  function ohlc4 (line 147) | async def ohlc4(context, symbol=None, time_frame=None, limit=-1, max_his...
  function Volume (line 162) | async def Volume(context, symbol=None, time_frame=None, limit=-1, max_hi...
  function get_candles_from_name (line 169) | async def get_candles_from_name(ctx, source_name="low", time_frame=None,...
  function _local_candles_manager (line 211) | async def _local_candles_manager(exchange_manager, symbol, time_frame, s...
  function _get_candle_manager (line 229) | async def _get_candle_manager(context, symbol, time_frame, max_history):
  function get_digits_adapted_price (line 254) | def get_digits_adapted_price(context, price, truncate=True):
  function get_digits_adapted_amount (line 259) | def get_digits_adapted_amount(context, amount, truncate=True):

FILE: Meta/Keywords/scripting_library/data/reading/metadata_reader.py
  class MetadataReader (line 20) | class MetadataReader(databases.DBReader):
    method read (line 21) | async def read(self) -> list:

FILE: Meta/Keywords/scripting_library/data/reading/trading_settings.py
  function set_initialized_evaluation (line 1) | def set_initialized_evaluation(ctx, trading_mode, initialized=True, symb...
  function get_initialized_evaluation (line 5) | def get_initialized_evaluation(ctx, trading_mode, symbol=None, time_fram...
  function are_all_evaluation_initialized (line 9) | def are_all_evaluation_initialized(ctx, trading_mode):

FILE: Meta/Keywords/scripting_library/data/writing/plotting.py
  function disable_candles_plot (line 24) | async def disable_candles_plot(ctx, time_frame=None):
  function plot (line 30) | async def plot(ctx, title, x=None,
  function plot_shape (line 182) | async def plot_shape(ctx, title, value, y_value,
  function _get_value_from_array (line 204) | def _get_value_from_array(array, index, multiplier=1):

FILE: Meta/Keywords/scripting_library/data/writing/portfolio.py
  function withdraw (line 21) | async def withdraw(context, amount, currency):

FILE: Meta/Keywords/scripting_library/errors.py
  class ScriptedLibraryError (line 1) | class ScriptedLibraryError(Exception):
  class InvalidBacktestingDataError (line 5) | class InvalidBacktestingDataError(ScriptedLibraryError):
  class MissingReadOnlyExchangeCredentialsError (line 9) | class MissingReadOnlyExchangeCredentialsError(ScriptedLibraryError):
  class InvalidProfileError (line 13) | class InvalidProfileError(ScriptedLibraryError):
  class InvalidTentacleProfileError (line 17) | class InvalidTentacleProfileError(InvalidProfileError):

FILE: Meta/Keywords/scripting_library/exchanges/local_exchange.py
  function local_ccxt_exchange_manager (line 11) | async def local_ccxt_exchange_manager(

FILE: Meta/Keywords/scripting_library/orders/cancelling.py
  function cancel_orders (line 21) | async def cancel_orders(

FILE: Meta/Keywords/scripting_library/orders/chaining.py
  function chain_order (line 19) | async def chain_order(base_order, chained_orders, update_with_triggering...

FILE: Meta/Keywords/scripting_library/orders/editing.py
  function edit_order (line 22) | async def edit_order(ctx, order,

FILE: Meta/Keywords/scripting_library/orders/grouping.py
  function create_one_cancels_the_other_group (line 20) | def create_one_cancels_the_other_group(context, group_identifier=None, o...
  function get_or_create_one_cancels_the_other_group (line 29) | def get_or_create_one_cancels_the_other_group(
  function create_balanced_take_profit_and_stop_group (line 43) | def create_balanced_take_profit_and_stop_group(context, group_identifier...
  function get_or_create_balanced_take_profit_and_stop_group (line 53) | def get_or_create_balanced_take_profit_and_stop_group(
  function add_orders_to_group (line 68) | def add_orders_to_group(ctx, order_group, orders):
  function get_group_from_orders (line 76) | def get_group_from_orders(orders, include_chained_orders=True):
  function get_open_orders_from_group (line 89) | def get_open_orders_from_group(order_group):
  function enable_group (line 93) | async def enable_group(order_group, enabled):
  function _create_order_group (line 97) | def _create_order_group(context, group_type, group_identifier, orders) -...
  function _get_or_create_order_group (line 104) | def _get_or_create_order_group(context, group_type, group_identifier) ->...

FILE: Meta/Keywords/scripting_library/orders/mocks.py
  function minimal_order_amount (line 6) | def minimal_order_amount(symbol):
  function max_digits (line 10) | def max_digits(symbol):
  function adapt_digits (line 14) | def adapt_digits(symbol, value):

FILE: Meta/Keywords/scripting_library/orders/open_orders.py
  function get_open_orders (line 18) | def get_open_orders(context):

FILE: Meta/Keywords/scripting_library/orders/order_tags.py
  function get_tagged_orders (line 18) | def get_tagged_orders(

FILE: Meta/Keywords/scripting_library/orders/order_types/create_order.py
  function create_order_instance (line 31) | async def create_order_instance(
  function _get_order_percents (line 101) | async def _get_order_percents(context, order_amount, order_target_positi...
  function _paired_order_is_closed (line 130) | def _paired_order_is_closed(context, group):
  function _use_total_holding (line 142) | def _use_total_holding(order_type_name):
  function _is_stop_order (line 146) | def _is_stop_order(order_type_name):
  function _get_order_quantity_and_side (line 150) | async def _get_order_quantity_and_side(context, order_amount, order_targ...
  function _get_order_details (line 179) | async def _get_order_details(context, order_type_name, side, order_offse...
  function _create_order (line 258) | async def _create_order(context, symbol, order_quantity, order_price, ta...
  function _get_group_adapted_quantity (line 363) | def _get_group_adapted_quantity(context, group, order_type, order_quanti...
  function _get_group_or_default (line 384) | def _get_group_or_default(context, group, stop_loss_price, take_profit_p...
  function _bundle_stop_loss_and_take_profit (line 395) | async def _bundle_stop_loss_and_take_profit(
  function _bundle_chained_order (line 428) | async def _bundle_chained_order(context, symbol_market, order, quantity,...

FILE: Meta/Keywords/scripting_library/orders/order_types/limit_order.py
  function limit (line 20) | async def limit(

FILE: Meta/Keywords/scripting_library/orders/order_types/market_order.py
  function market (line 20) | async def market(

FILE: Meta/Keywords/scripting_library/orders/order_types/scaled_order.py
  function scaled_limit (line 21) | async def scaled_limit(
  function scaled_stop_loss (line 127) | async def scaled_stop_loss(

FILE: Meta/Keywords/scripting_library/orders/order_types/stop_loss_order.py
  function stop_loss (line 20) | async def stop_loss(

FILE: Meta/Keywords/scripting_library/orders/order_types/trailing_limit_order.py
  function trailing_limit (line 20) | async def trailing_limit(

FILE: Meta/Keywords/scripting_library/orders/order_types/trailing_market_order.py
  function trailing_market (line 20) | async def trailing_market(

FILE: Meta/Keywords/scripting_library/orders/order_types/trailing_stop_loss_order.py
  function trailing_stop_loss (line 20) | async def trailing_stop_loss(

FILE: Meta/Keywords/scripting_library/orders/position_size/amount.py
  function get_amount (line 24) | async def get_amount(

FILE: Meta/Keywords/scripting_library/orders/position_size/target_position.py
  function get_target_position (line 25) | async def get_target_position(
  function get_target_position_side (line 72) | def get_target_position_side(order_size):

FILE: Meta/Keywords/scripting_library/orders/waiting.py
  function wait_for_orders_close (line 24) | async def wait_for_orders_close(ctx, orders, timeout=None):
  function are_all_chained_orders_created (line 45) | def are_all_chained_orders_created(ctx, orders):
  function wait_for_stop_loss_open (line 63) | async def wait_for_stop_loss_open(ctx, order_tag=None, order_group=None,...

FILE: Meta/Keywords/scripting_library/settings/script_settings.py
  function set_minimum_candles (line 20) | def set_minimum_candles(context, candles_count):
  function do_not_initialize (line 34) | def do_not_initialize():
  function set_allow_artificial_orders (line 38) | def set_allow_artificial_orders(context, allow_artificial_orders):

FILE: Meta/Keywords/scripting_library/tests/__init__.py
  function null_context (line 30) | def null_context():
  function mock_context (line 52) | async def mock_context(backtesting_trader):
  function symbol_market (line 96) | def symbol_market():
  function event_loop (line 120) | def event_loop():
  function skip_if_octobot_trading_mocking_disabled (line 134) | def skip_if_octobot_trading_mocking_disabled(request):
  function _configure_async_test_loop (line 143) | def _configure_async_test_loop():

FILE: Meta/Keywords/scripting_library/tests/backtesting/data_store.py
  function default_price_data (line 23) | def default_price_data():
  function default_trades_data (line 31) | def default_trades_data():
  function default_portfolio_historical_value (line 93) | def default_portfolio_historical_value():
  function default_portfolio_data (line 99) | def default_portfolio_data():
  function default_spot_metadata (line 104) | def default_spot_metadata():
  function default_pnl_historical_value (line 112) | def default_pnl_historical_value():
  function default_funding_fees_data (line 119) | def default_funding_fees_data():
  function default_realized_pnl_history (line 125) | def default_realized_pnl_history():

FILE: Meta/Keywords/scripting_library/tests/backtesting/test_backtesting_data_collector.py
  class DummyLogger (line 26) | class DummyLogger:
    method __init__ (line 27) | def __init__(self):
    method info (line 31) | def info(self, msg):
    method error (line 33) | def error(self, msg):
    method exception (line 35) | def exception(self, err, *args, **kwargs):
  function patch_logger (line 38) | def patch_logger(monkeypatch):
  function base_args (line 43) | def base_args():
  function test_ensure_compatible_candle_time_normal_case (line 56) | def test_ensure_compatible_candle_time_normal_case(monkeypatch):
  function test_ensure_compatible_candle_time_starts_too_early (line 75) | def test_ensure_compatible_candle_time_starts_too_early():
  function test_ensure_compatible_candle_time_starts_too_late_and_required (line 92) | def test_ensure_compatible_candle_time_starts_too_late_and_required():
  function test_ensure_compatible_candle_time_starts_too_late_but_adapted_with_test_data (line 110) | def test_ensure_compatible_candle_time_starts_too_late_but_adapted_with_...
  function test_ensure_compatible_candle_time_starts_too_late_but_adapted_with_real_data_dca (line 129) | def test_ensure_compatible_candle_time_starts_too_late_but_adapted_with_...
  function test_ensure_compatible_candle_time_starts_too_late_but_adapted_with_real_data_basked (line 160) | def test_ensure_compatible_candle_time_starts_too_late_but_adapted_with_...
  function test_ensure_compatible_candle_time_ends_too_late (line 179) | def test_ensure_compatible_candle_time_ends_too_late():
  function test_ensure_compatible_candle_time_ends_too_early_and_required (line 196) | def test_ensure_compatible_candle_time_ends_too_early_and_required():
  function test_ensure_compatible_candle_time_ends_too_early_but_not_required (line 213) | def test_ensure_compatible_candle_time_ends_too_early_but_not_required(m...
  function test_ensure_compatible_candle_time_adapted_start_time_too_short (line 231) | def test_ensure_compatible_candle_time_adapted_start_time_too_short():

FILE: Meta/Keywords/scripting_library/tests/backtesting/test_collect_data_and_run_backtesting.py
  function trading_mode_tentacles_data (line 32) | def trading_mode_tentacles_data() -> commons_profile_data.TentaclesData:
  function test_collect_candles_without_backend_and_run_backtesting (line 66) | async def test_collect_candles_without_backend_and_run_backtesting(tradi...

FILE: Meta/Keywords/scripting_library/tests/backtesting/test_run_data.py
  function test_plot_historical_portfolio_value (line 32) | async def test_plot_historical_portfolio_value(default_price_data, defau...
  function test_get_historical_pnl (line 44) | async def test_get_historical_pnl(default_price_data, default_trades_dat...
  function test_total_paid_fees (line 72) | async def test_total_paid_fees(default_trades_data):
  function _test_historical_portfolio_values (line 86) | async def _test_historical_portfolio_values(price_data, trades_data, por...
  function _test_historical_pnl_values_from_trades (line 111) | async def _test_historical_pnl_values_from_trades(price_data, trades_dat...

FILE: Meta/Keywords/scripting_library/tests/configuration/__init__.py
  function backtesting_config (line 23) | async def backtesting_config(request):
  function fake_backtesting (line 34) | async def fake_backtesting(backtesting_config):
  function backtesting_exchange_manager (line 44) | async def backtesting_exchange_manager(request, backtesting_config, fake...
  function backtesting_trader (line 69) | async def backtesting_trader(backtesting_config, backtesting_exchange_ma...

FILE: Meta/Keywords/scripting_library/tests/configuration/test_indexes_configuration.py
  function test_create_index_config_from_tentacles_config (line 24) | def test_create_index_config_from_tentacles_config():
  function test_generate_index_config (line 110) | def test_generate_index_config():

FILE: Meta/Keywords/scripting_library/tests/configuration/test_profile_data_configuration.py
  function test_register_historical_configs_adds_traded_pairs (line 24) | def test_register_historical_configs_adds_traded_pairs():
  function test_register_historical_configs_registers_historical_tentacle_config (line 40) | def test_register_historical_configs_registers_historical_tentacle_confi...
  function test_register_historical_configs_applies_master_edits (line 63) | def test_register_historical_configs_applies_master_edits():

FILE: Meta/Keywords/scripting_library/tests/exchanges/__init__.py
  function backtesting_config (line 23) | async def backtesting_config(request):
  function fake_backtesting (line 34) | async def fake_backtesting(backtesting_config):
  function backtesting_exchange_manager (line 44) | async def backtesting_exchange_manager(request, backtesting_config, fake...
  function backtesting_trader (line 69) | async def backtesting_trader(backtesting_config, backtesting_exchange_ma...

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_create_order.py
  function test_create_order_instance (line 41) | async def test_create_order_instance(mock_context):
  function test_paired_order_is_closed (line 94) | async def test_paired_order_is_closed(mock_context, skip_if_octobot_trad...
  function test_use_total_holding (line 129) | async def test_use_total_holding():
  function test_is_stop_order (line 138) | async def test_is_stop_order():
  function test_get_order_quantity_and_side (line 150) | async def test_get_order_quantity_and_side(null_context):
  function test_get_order_details (line 219) | async def test_get_order_details(null_context):
  function test_create_order (line 289) | async def test_create_order(mock_context, symbol_market):
  function test_get_group_adapted_quantity (line 391) | async def test_get_group_adapted_quantity(mock_context, skip_if_octobot_...

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_limit_order.py
  function test_limit (line 30) | async def test_limit(null_context):

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_market_order.py
  function test_market (line 30) | async def test_market(null_context):

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_multiple_orders_creation.py
  function test_orders_with_invalid_values (line 44) | async def test_orders_with_invalid_values(mock_context, skip_if_octobot_...
  function test_orders_amount_then_position_sequence (line 104) | async def test_orders_amount_then_position_sequence(mock_context):
  function test_concurrent_orders (line 199) | async def test_concurrent_orders(mock_context):
  function test_sell_limit_with_stop_loss_orders_single_sell_and_stop_with_oco_group (line 263) | async def test_sell_limit_with_stop_loss_orders_single_sell_and_stop_wit...
  function test_sell_limit_with_stop_loss_orders_two_sells_and_stop_with_oco (line 293) | async def test_sell_limit_with_stop_loss_orders_two_sells_and_stop_with_...
  function test_sell_limit_with_multiple_stop_loss_and_sell_orders_in_balanced_take_profit_and_stop_group (line 338) | async def test_sell_limit_with_multiple_stop_loss_and_sell_orders_in_bal...
  function test_multiple_sell_limit_with_stop_loss_rounding_issues_in_balanced_take_profit_and_stop_group (line 407) | async def test_multiple_sell_limit_with_stop_loss_rounding_issues_in_bal...
  function _usdt_trading_context (line 449) | async def _usdt_trading_context(mock_context):
  function _20_percent_position_trading_context (line 465) | async def _20_percent_position_trading_context(mock_context):
  function _fill_and_check (line 484) | async def _fill_and_check(mock_context, btc_available, usdt_available, o...
  function _ensure_orders_validity (line 497) | def _ensure_orders_validity(mock_context, btc_available, usdt_available,...

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_stop_loss_order.py
  function test_stop_loss (line 30) | async def test_stop_loss(null_context):

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_trailing_limit_order.py
  function test_trailing_limit (line 30) | async def test_trailing_limit(null_context):

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_trailing_market_order.py
  function test_trailing_market (line 30) | async def test_trailing_market(null_context):

FILE: Meta/Keywords/scripting_library/tests/orders/order_types/test_trailing_stop_loss_order.py
  function test_trailing_stop_loss (line 30) | async def test_trailing_stop_loss(null_context):

FILE: Meta/Keywords/scripting_library/tests/orders/position_size/test_target_position.py
  function test_get_target_position_side (line 31) | def test_get_target_position_side():
  function test_get_target_position (line 39) | async def test_get_target_position(mock_context):

FILE: Meta/Keywords/scripting_library/tests/orders/test_cancelling.py
  function test_cancel_orders (line 35) | async def test_cancel_orders(mock_context, skip_if_octobot_trading_mocki...

FILE: Meta/Keywords/scripting_library/tests/test_utils/order_util.py
  function fill_limit_or_stop_order (line 19) | async def fill_limit_or_stop_order(limit_or_stop_order):
  function fill_market_order (line 24) | async def fill_market_order(market_order):

FILE: Services/Interfaces/telegram_bot_interface/telegram_bot.py
  class TelegramBotInterface (line 34) | class TelegramBotInterface(interfaces_bots.AbstractBotInterface):
    method __init__ (line 40) | def __init__(self, config):
    method _post_initialize (line 44) | async def _post_initialize(self, _):
    method _inner_start (line 52) | async def _inner_start(self) -> bool:
    method stop (line 63) | async def stop(self):
    method get_bot_handlers (line 66) | def get_bot_handlers(self):
    method command_unknown (line 90) | async def command_unknown(update: telegram.Update, _: telegram.ext.Con...
    method command_help (line 99) | async def command_help(update: telegram.Update, _: telegram.ext.Contex...
    method command_start (line 131) | async def command_start(update: telegram.Update, _: telegram.ext.Conte...
    method command_restart (line 140) | async def command_restart(update: telegram.Update, _: telegram.ext.Con...
    method command_stop (line 149) | async def command_stop(update: telegram.Update, _: telegram.ext.Contex...
    method command_version (line 162) | async def command_version(update: telegram.Update, _: telegram.ext.Con...
    method command_pause_resume (line 168) | async def command_pause_resume(self, update: telegram.Update, _: teleg...
    method command_ping (line 183) | async def command_ping(update: telegram.Update, _: telegram.ext.Contex...
    method command_risk (line 190) | async def command_risk(update: telegram.Update, context: telegram.ext....
    method command_profitability (line 201) | async def command_profitability(update: telegram.Update, _: telegram.e...
    method command_fees (line 208) | async def command_fees(update: telegram.Update, _: telegram.ext.Contex...
    method command_sell_all_currencies (line 215) | async def command_sell_all_currencies(update: telegram.Update, _: tele...
    method command_sell_all (line 222) | async def command_sell_all(update: telegram.Update, context: telegram....
    method command_portfolio (line 233) | async def command_portfolio(update: telegram.Update, _: telegram.ext.C...
    method command_open_orders (line 239) | async def command_open_orders(update: telegram.Update, _: telegram.ext...
    method command_trades_history (line 246) | async def command_trades_history(update: telegram.Update, _: telegram....
    method command_portfolio_refresh (line 254) | async def command_portfolio_refresh(update: telegram.Update, _: telegr...
    method command_configuration (line 265) | async def command_configuration(update: telegram.Update, _: telegram.e...
    method command_market_status (line 280) | async def command_market_status(update: telegram.Update, _: telegram.e...
    method command_error (line 294) | async def command_error(update: telegram.Update, context: telegram.ext...
    method handle_polling_error (line 309) | def handle_polling_error(error):
    method get_error_log_level (line 329) | def get_error_log_level(error):
    method echo (line 341) | async def echo(update: telegram.Update, _: telegram.ext.ContextTypes.D...
    method enable (line 346) | def enable(config, is_enabled, associated_config=services_constants.CO...
    method is_enabled (line 350) | def is_enabled(config, associated_config=services_constants.CONFIG_TEL...
    method _is_authorized_chat (line 354) | def _is_authorized_chat(update: telegram.Update):
    method _is_valid_user (line 358) | def _is_valid_user(update: telegram.Update, associated_config=services...
    method _send_message (line 373) | async def _send_message(update: telegram.Update, message: str, markdow...

FILE: Services/Interfaces/telegram_bot_interface/tests/test_bot_interface.py
  function create_minimalist_unconnected_octobot (line 36) | async def create_minimalist_unconnected_octobot():
  function get_bot_interface (line 56) | async def get_bot_interface():
  function test_all_commands (line 65) | async def test_all_commands():

FILE: Services/Interfaces/web_interface/__init__.py
  class Notifier (line 26) | class Notifier:
    method send_notifications (line 28) | def send_notifications(self) -> bool:
  function register_notifier (line 35) | def register_notifier(notification_key, notifier):
  function dir_last_updated (line 68) | def dir_last_updated(folder):
  function update_registered_plugins (line 80) | def update_registered_plugins(plugins):
  function flush_notifications (line 95) | def flush_notifications():
  function _send_notification (line 99) | def _send_notification(notification_key, **kwargs) -> bool:
  function send_general_notifications (line 106) | def send_general_notifications(**kwargs):
  function send_backtesting_status (line 111) | def send_backtesting_status(**kwargs):
  function send_data_collector_status (line 115) | def send_data_collector_status(**kwargs):
  function send_strategy_optimizer_status (line 119) | def send_strategy_optimizer_status(**kwargs):
  function send_new_trade (line 123) | def send_new_trade(dict_new_trade, exchange_id, symbol):
  function send_order_update (line 127) | def send_order_update(dict_order, exchange_id, symbol):
  function add_notification (line 131) | async def add_notification(level: services_enums.NotificationLevel, titl...
  function get_notifications (line 146) | def get_notifications() -> list:
  function get_notifications_history (line 150) | def get_notifications_history() -> list:
  function get_critical_notifications (line 154) | def get_critical_notifications() -> list:
  function get_logs (line 158) | def get_logs():
  function get_errors_count (line 162) | def get_errors_count():
  function flush_errors_count (line 166) | def flush_errors_count():

FILE: Services/Interfaces/web_interface/advanced_controllers/__init__.py
  function register (line 26) | def register(distribution: octobot.enums.OctoBotDistribution):

FILE: Services/Interfaces/web_interface/advanced_controllers/configuration.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/advanced_controllers/home.py
  function register (line 21) | def register(blueprint):

FILE: Services/Interfaces/web_interface/advanced_controllers/matrix.py
  function register (line 22) | def register(blueprint):

FILE: Services/Interfaces/web_interface/advanced_controllers/strategy_optimizer.py
  function register (line 23) | def register(blueprint):

FILE: Services/Interfaces/web_interface/advanced_controllers/tentacles_management.py
  function register (line 25) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/__init__.py
  function register (line 37) | def register(distribution: octobot.enums.OctoBotDistribution):

FILE: Services/Interfaces/web_interface/api/bots.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/config.py
  function register (line 26) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/dsl.py
  function register (line 22) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/exchanges.py
  function register (line 23) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/feedback.py
  function register (line 23) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/metadata.py
  function register (line 27) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/tentacles_packages.py
  function register (line 23) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/trading.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/user_commands.py
  function register (line 23) | def register(blueprint):

FILE: Services/Interfaces/web_interface/api/webhook.py
  function register_webhook (line 24) | def register_webhook(callback):
  function has_webhook (line 28) | def has_webhook(callback):
  function register (line 32) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/__init__.py
  function register (line 45) | def register(blueprint, distribution: octobot.enums.OctoBotDistribution):

FILE: Services/Interfaces/web_interface/controllers/about.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/automation.py
  function register (line 28) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/backtesting.py
  function register (line 28) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/commands.py
  function register (line 22) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/community.py
  function register (line 26) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/community_authentication.py
  function register (line 31) | def register(blueprint):
  class CommunityLoginForm (line 128) | class CommunityLoginForm(flask_wtf.FlaskForm):

FILE: Services/Interfaces/web_interface/controllers/configuration.py
  function register (line 35) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/dashboard.py
  function register (line 22) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/distributions/market_making/__init__.py
  function register (line 25) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/distributions/market_making/cloud.py
  function register (line 21) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/distributions/market_making/configuration.py
  function register (line 29) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/distributions/market_making/dashboard.py
  function register (line 30) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/dsl.py
  function register (line 21) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/errors.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/home.py
  function register (line 30) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/interface_settings.py
  function register (line 23) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/logs.py
  function register (line 28) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/medias.py
  function _send_file (line 23) | def _send_file(base_dir, file_path):
  function register (line 28) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/octobot_authentication.py
  function register (line 29) | def register(blueprint):
  class LoginForm (line 71) | class LoginForm(flask_wtf.FlaskForm):

FILE: Services/Interfaces/web_interface/controllers/octobot_help.py
  function register (line 21) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/portfolio.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/profiles.py
  function register (line 30) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/reboot.py
  function register (line 22) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/robots.py
  function register (line 19) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/tentacles_config.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/terms.py
  function register (line 24) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/trading.py
  function register (line 26) | def register(blueprint):

FILE: Services/Interfaces/web_interface/controllers/welcome.py
  function register (line 21) | def register(blueprint):

FILE: Services/Interfaces/web_interface/enums.py
  class PriceStrings (line 19) | class PriceStrings(enum.Enum):
  class TabsLocation (line 28) | class TabsLocation(enum.Enum):
  class ColorModes (line 33) | class ColorModes(enum.Enum):

FILE: Services/Interfaces/web_interface/errors.py
  class MissingExchangeId (line 18) | class MissingExchangeId(Exception):

FILE: Services/Interfaces/web_interface/flask_util/browsing_data_provider.py
  class BrowsingDataProvider (line 37) | class BrowsingDataProvider(singleton.Singleton):
    method __init__ (line 51) | def __init__(self):
    method get_distribution_key (line 57) | def get_distribution_key(distribution: octobot.enums.OctoBotDistributi...
    method get_or_create_session_secret_key (line 60) | def get_or_create_session_secret_key(self):
    method get_and_unset_is_first_display (line 70) | def get_and_unset_is_first_display(self, element):
    method set_is_first_display (line 79) | def set_is_first_display(self, element, is_first_display):
    method set_first_displays (line 88) | def set_first_displays(self, is_first_display):
    method get_currency_logo_url (line 93) | def get_currency_logo_url(self, currency_id):
    method set_currency_logo_url (line 99) | def set_currency_logo_url(self, currency_id, url, dump=True):
    method get_all_currencies (line 107) | def get_all_currencies(self):
    method set_all_currencies (line 110) | def set_all_currencies(self, all_currencies):
    method _get_session_secret_key (line 114) | def _get_session_secret_key(self):
    method _create_session_secret_key (line 125) | def _create_session_secret_key(self):
    method _generate_session_secret_key (line 130) | def _generate_session_secret_key(self):
    method _get_default_data (line 134) | def _get_default_data(self):
    method _apply_saved_data (line 142) | def _apply_saved_data(self, read_data):
    method _load_saved_data (line 148) | def _load_saved_data(self):
    method dump_saved_data (line 162) | def dump_saved_data(self):
    method _get_file (line 169) | def _get_file(self):
    method _get_expiring_cached_value (line 172) | def _get_expiring_cached_value(self, key):
    method _set_expiring_cached_value (line 176) | def _set_expiring_cached_value(self, key, value):
    method _create_expiring_cached_value (line 179) | def _create_expiring_cached_value(self, value):
    method _ensure_cache_expiration (line 185) | def _ensure_cache_expiration(self, key):

FILE: Services/Interfaces/web_interface/flask_util/content_types_management.py
  function init_content_types (line 20) | def init_content_types():

FILE: Services/Interfaces/web_interface/flask_util/context_processor.py
  function register_context_processor (line 35) | def register_context_processor(web_interface_instance):

FILE: Services/Interfaces/web_interface/flask_util/cors.py
  function get_user_defined_cors_allowed_origins (line 21) | def get_user_defined_cors_allowed_origins():

FILE: Services/Interfaces/web_interface/flask_util/file_services.py
  function send_and_remove_file (line 21) | def send_and_remove_file(file_path, download_name):

FILE: Services/Interfaces/web_interface/flask_util/json_provider.py
  class FloatDecimalJSONProvider (line 21) | class FloatDecimalJSONProvider(flask.json.provider.DefaultJSONProvider):
    method dumps (line 23) | def dumps(self, obj, **kwargs):

FILE: Services/Interfaces/web_interface/flask_util/template_filters.py
  function register_template_filters (line 18) | def register_template_filters(app):

FILE: Services/Interfaces/web_interface/login/open_source_package_required.py
  function open_source_package_required (line 23) | def open_source_package_required(func):

FILE: Services/Interfaces/web_interface/login/user.py
  class User (line 18) | class User:
    method __init__ (line 24) | def __init__(self):
    method get_id (line 35) | def get_id(self):

FILE: Services/Interfaces/web_interface/login/web_login_manager.py
  class WebLoginManager (line 33) | class WebLoginManager(flask_login.LoginManager):
    method __init__ (line 34) | def __init__(self, flask_app, password_hash):
    method login_user (line 44) | def login_user(self, remember=False, duration=None, **kwargs):
    method is_valid_password (line 49) | def is_valid_password(self, ip, password, form):
    method _register_callbacks (line 69) | def _register_callbacks(self):
  function is_authenticated (line 76) | def is_authenticated():
  function set_is_login_required (line 80) | def set_is_login_required(login_required):
  function is_login_required (line 85) | def is_login_required():
  function _login_required_func (line 90) | def _login_required_func(func, *args, **kwargs):
  function login_required_when_activated (line 94) | def login_required_when_activated(func):
  function active_login_required (line 103) | def active_login_required(func):
  function register_attempt (line 115) | def register_attempt(ip):
  function is_banned (line 123) | def is_banned(ip):
  function reset_attempts (line 129) | def reset_attempts(ip):

FILE: Services/Interfaces/web_interface/models/backtesting.py
  function get_full_candle_history_exchange_list (line 55) | def get_full_candle_history_exchange_list():
  function get_other_history_exchange_list (line 60) | def get_other_history_exchange_list():
  function _get_description (line 65) | async def _get_description(data_file, files_with_description):
  function _is_usable_description (line 71) | def _is_usable_description(description):
  function _retrieve_data_files_with_description (line 77) | async def _retrieve_data_files_with_description(files):
  function get_data_files_with_description (line 87) | def get_data_files_with_description():
  function start_backtesting_using_specific_files (line 92) | def start_backtesting_using_specific_files(files, source, reset_tentacle...
  function start_backtesting_using_current_bot_data (line 104) | def start_backtesting_using_current_bot_data(data_source, exchange_id, s...
  function stop_previous_backtesting (line 122) | def stop_previous_backtesting():
  function is_backtesting_enabled (line 133) | def is_backtesting_enabled():
  function _parse_trading_type (line 137) | def _parse_trading_type(trading_type):
  function _start_backtesting (line 151) | def _start_backtesting(files, source, reset_tentacle_config=False, run_o...
  function _collect_initialize_and_run_independent_backtesting (line 238) | async def _collect_initialize_and_run_independent_backtesting(
  function get_backtesting_status (line 295) | def get_backtesting_status():
  function get_backtesting_report (line 308) | def get_backtesting_report(source):
  function get_latest_backtesting_run_id (line 322) | def get_latest_backtesting_run_id(trading_mode):
  function get_delete_data_file (line 337) | def get_delete_data_file(file_name):
  function get_data_collector_status (line 345) | def get_data_collector_status():
  function stop_data_collector (line 362) | def stop_data_collector():
  function create_snapshot_data_collector (line 372) | def create_snapshot_data_collector(exchange_id, start_timestamp, end_tim...
  function get_data_files_from_current_bot (line 421) | def get_data_files_from_current_bot(exchange_id, start_timestamp, end_ti...
  function collect_data_file (line 437) | def collect_data_file(exchange, symbols, time_frames=None, start_timesta...
  function _start_collect_and_notify (line 466) | async def _start_collect_and_notify(data_collector_instance):
  function _background_collect_exchange_historical_data (line 478) | def _background_collect_exchange_historical_data(exchange, exchange_type...
  function _convert_into_octobot_data_file_if_necessary (line 495) | async def _convert_into_octobot_data_file_if_necessary(output_file):
  function save_data_file (line 517) | def save_data_file(name, file):
  function _ensure_backtesting_limits (line 530) | def _ensure_backtesting_limits(exchange, symbols, time_frames, start_tim...

FILE: Services/Interfaces/web_interface/models/commands.py
  function schedule_delayed_command (line 25) | def schedule_delayed_command(command, *args, delay=0.5):
  function restart_bot (line 32) | def restart_bot(delay=None):
  function is_rebooting (line 42) | def is_rebooting():
  function stop_bot (line 46) | def stop_bot():
  function update_bot (line 50) | def update_bot():

FILE: Services/Interfaces/web_interface/models/community.py
  function get_community_metrics_to_display (line 27) | def get_community_metrics_to_display():
  function can_get_community_metrics (line 31) | def can_get_community_metrics():
  function get_owned_packages (line 35) | def get_owned_packages() -> list[str]:
  function has_owned_packages_to_install (line 40) | def has_owned_packages_to_install() -> list[str]:
  function update_owned_packages (line 45) | def update_owned_packages():
  function has_open_source_package (line 50) | def has_open_source_package() -> bool:
  function get_checkout_url (line 55) | def get_checkout_url(payment_method, redirect_url) -> (bool, str):
  function get_tradingview_email_address (line 65) | def get_tradingview_email_address() -> str:
  function get_last_email_address_confirm_code_email_content (line 69) | def get_last_email_address_confirm_code_email_content() -> typing.Option...
  function wait_for_email_address_confirm_code_email (line 73) | def wait_for_email_address_confirm_code_email():
  function get_cloud_strategies (line 79) | def get_cloud_strategies(authenticator) -> list[octobot_community.Strate...
  function get_cloud_strategy (line 83) | def get_cloud_strategy(authenticator, strategy_id: str) -> octobot_commu...
  function get_preview_tentacles_packages (line 87) | def get_preview_tentacles_packages(url_for):
  function get_current_octobots_stats (line 109) | def get_current_octobots_stats():
  function _format_bot (line 113) | def _format_bot(bot):
  function get_all_user_bots (line 120) | def get_all_user_bots():
  function get_selected_user_bot (line 129) | def get_selected_user_bot():
  function select_bot (line 133) | def select_bot(bot_id):
  function create_new_bot (line 137) | def create_new_bot():
  function can_select_bot (line 141) | def can_select_bot():
  function can_logout (line 145) | def can_logout():
  function get_user_account_id (line 149) | def get_user_account_id():
  function has_filled_form (line 153) | def has_filled_form(form_id):
  function register_user_submitted_form (line 157) | def register_user_submitted_form(user_id, form_id):
  function get_followed_strategy_url (line 169) | def get_followed_strategy_url():
  function is_community_feed_connected (line 181) | def is_community_feed_connected():
  function get_last_signal_time (line 185) | def get_last_signal_time():
  function _sync_community_account (line 189) | async def _sync_community_account():
  function sync_community_account (line 194) | def sync_community_account():
  function wait_for_login_if_processing (line 198) | def wait_for_login_if_processing():

FILE: Services/Interfaces/web_interface/models/configuration.py
  function _get_currency_dict (line 123) | def _get_currency_dict(name, symbol, identifier):
  function _get_logger (line 138) | def _get_logger():
  function _get_evaluators_tentacles_activation (line 145) | def _get_evaluators_tentacles_activation():
  function _get_trading_tentacles_activation (line 153) | def _get_trading_tentacles_activation():
  function _get_services_tentacles_activation (line 161) | def _get_services_tentacles_activation():
  function get_evaluators_tentacles_startup_activation (line 169) | def get_evaluators_tentacles_startup_activation():
  function get_trading_tentacles_startup_activation (line 177) | def get_trading_tentacles_startup_activation():
  function get_tentacle_documentation (line 185) | def get_tentacle_documentation(name, media_url, missing_tentacles: set =...
  function _get_strategy_activation_state (line 204) | def _get_strategy_activation_state(
  function _add_to_missing_tentacles_if_missing (line 255) | def _add_to_missing_tentacles_if_missing(tentacle_name: str, missing_ten...
  function _get_tentacle_packages (line 267) | def _get_tentacle_packages():
  function _get_activation_state (line 286) | def _get_activation_state(name, activation_states):
  function is_trading_strategy_configuration (line 290) | def is_trading_strategy_configuration(tentacle_type):
  function get_tentacle_from_string (line 296) | def get_tentacle_from_string(name, media_url, with_info=True):
  function get_tentacle_user_commands (line 328) | def get_tentacle_user_commands(klass):
  function get_tentacle_config_and_user_inputs (line 332) | async def get_tentacle_config_and_user_inputs(tentacle_class, bot_config...
  function get_tentacle_config_and_edit_display (line 340) | def get_tentacle_config_and_edit_display(tentacle, tentacle_class=None, ...
  function are_automations_enabled (line 359) | def are_automations_enabled():
  function is_advanced_interface_enabled (line 363) | def is_advanced_interface_enabled():
  function restart_global_automations (line 367) | def restart_global_automations():
  function get_all_automation_steps (line 374) | def get_all_automation_steps():
  function has_at_least_one_running_automation (line 378) | def has_at_least_one_running_automation():
  function get_automations_count (line 382) | def get_automations_count():
  function reset_automation_config_to_default (line 386) | def reset_automation_config_to_default():
  function get_tentacle_config (line 394) | def get_tentacle_config(klass):
  function get_cached_tentacle_config (line 398) | def get_cached_tentacle_config(klass):
  function get_tentacle_config_schema (line 410) | def get_tentacle_config_schema(klass):
  function _get_tentacle_activation_desc (line 419) | def _get_tentacle_activation_desc(name, activated, startup_val, media_ur...
  function _add_tentacles_activation_desc_for_group (line 428) | def _add_tentacles_activation_desc_for_group(activation_by_group, tentac...
  function get_extra_tentacles_config_desc (line 445) | def get_extra_tentacles_config_desc(media_url, missing_tentacles: set):
  function get_tentacles_activation_desc_by_group (line 466) | def get_tentacles_activation_desc_by_group(media_url, missing_tentacles:...
  function update_tentacle_config (line 484) | def update_tentacle_config(tentacle_name, config_update, tentacle_class=...
  function update_copied_trading_id (line 500) | def update_copied_trading_id(copy_id):
  function reset_config_to_default (line 510) | def reset_config_to_default(tentacle_name, tentacle_class=None, tentacle...
  function _get_required_element (line 527) | def _get_required_element(elements_config):
  function _add_strategy_requirements_and_default_config (line 538) | def _add_strategy_requirements_and_default_config(desc, klass):
  function _add_trading_mode_requirements_and_default_config (line 549) | def _add_trading_mode_requirements_and_default_config(desc, klass):
  function _add_strategies_requirements (line 565) | def _add_strategies_requirements(strategies, strategy_config):
  function _add_trading_modes_requirements (line 572) | def _add_trading_modes_requirements(trading_modes_list, strategy_config):
  function get_strategy_config (line 580) | def get_strategy_config(
  function get_in_backtesting_mode (line 594) | def get_in_backtesting_mode():
  function accepted_terms (line 598) | def accepted_terms():
  function accept_terms (line 602) | def accept_terms(accepted):
  function _fill_evaluator_config (line 606) | def _fill_evaluator_config(evaluator_name, activated, eval_type_key,
  function get_evaluator_detailed_config (line 624) | def get_evaluator_detailed_config(media_url, missing_tentacles: set, sin...
  function get_config_activated_trading_mode (line 676) | def get_config_activated_trading_mode(tentacles_setup_config=None):
  function get_config_activated_strategies (line 685) | def get_config_activated_strategies(tentacles_setup_config=None):
  function get_config_activated_evaluators (line 691) | def get_config_activated_evaluators(tentacles_setup_config=None):
  function has_futures_exchange (line 697) | def has_futures_exchange():
  function update_tentacles_activation_config (line 704) | def update_tentacles_activation_config(new_config, deactivate_others=Fal...
  function get_active_exchanges (line 721) | def get_active_exchanges():
  function _reset_profile_portfolio_history (line 725) | async def _reset_profile_portfolio_history(current_edited_config):
  function _handle_special_fields (line 756) | def _handle_special_fields(current_edited_config, new_config):
  function _handle_simulated_portfolio (line 779) | def _handle_simulated_portfolio(current_edited_config, new_config):
  function update_global_config (line 794) | def update_global_config(new_config, delete=False):
  function activate_metrics (line 810) | def activate_metrics(enable_metrics):
  function activate_beta_env (line 823) | def activate_beta_env(enable_beta):
  function get_metrics_enabled (line 834) | def get_metrics_enabled():
  function get_beta_env_enabled_in_config (line 838) | def get_beta_env_enabled_in_config():
  function get_services_list (line 844) | def get_services_list():
  function get_notifiers_list (line 854) | def get_notifiers_list():
  function get_enabled_trading_pairs (line 860) | def get_enabled_trading_pairs() -> set:
  function get_exchange_available_trading_pairs (line 868) | def get_exchange_available_trading_pairs(exchange_manager, profile=None)...
  function get_symbol_list (line 876) | def get_symbol_list(exchanges):
  function get_all_currencies (line 881) | def get_all_currencies(exchanges):
  function _get_filtered_exchange_symbols (line 891) | def _get_filtered_exchange_symbols(symbols):
  function _load_market (line 895) | async def _load_market(exchange, results):
  function _add_merged_exchanges (line 915) | def _add_merged_exchanges(exchanges):
  function _load_markets (line 924) | async def _load_markets(exchanges):
  function get_config_time_frames (line 953) | def get_config_time_frames() -> list:
  function get_timeframes_list (line 957) | def get_timeframes_list(exchanges):
  function get_strategy_required_time_frames (line 971) | def get_strategy_required_time_frames(strategy_class, tentacles_setup_co...
  function format_config_symbols (line 978) | def format_config_symbols(config):
  function format_config_symbols_without_enabled_key (line 986) | def format_config_symbols_without_enabled_key(config):
  function _is_legit_currency (line 996) | def _is_legit_currency(currency):
  function get_all_symbols_list (line 1000) | def get_all_symbols_list():
  function get_all_symbols_list_by_symbol_type (line 1042) | def get_all_symbols_list_by_symbol_type(all_symbols, config_symbols):
  function get_exchange_logo (line 1074) | def get_exchange_logo(exchange_name):
  function _get_currency_logo_url (line 1096) | def _get_currency_logo_url(currency_id):
  function _fetch_currency_logo (line 1101) | async def _fetch_currency_logo(session, data_provider, currency_id):
  function _fetch_missing_currency_logos (line 1120) | async def _fetch_missing_currency_logos(data_provider, currency_ids):
  function get_currency_logo_urls (line 1133) | def get_currency_logo_urls(currency_ids):
  function get_traded_time_frames (line 1150) | def get_traded_time_frames(exchange_manager, strategies=None, tentacles_...
  function get_or_init_FULL_EXCHANGE_LIST (line 1166) | def get_or_init_FULL_EXCHANGE_LIST():
  function auto_filled_exchanges (line 1177) | def auto_filled_exchanges(tentacles_setup_config=None):
  function get_full_exchange_list (line 1191) | def get_full_exchange_list(tentacles_setup_config=None):
  function get_full_configurable_exchange_list (line 1196) | def get_full_configurable_exchange_list(remove_config_exchanges=False):
  function get_default_exchange (line 1212) | def get_default_exchange():
  function get_tested_exchange_list (line 1216) | def get_tested_exchange_list():
  function get_simulated_exchange_list (line 1224) | def get_simulated_exchange_list():
  function get_other_exchange_list (line 1232) | def get_other_exchange_list(remove_config_exchanges=False):
  function get_enabled_exchange_types (line 1242) | def get_enabled_exchange_types(config_exchanges):
  function get_exchanges_details (line 1250) | def get_exchanges_details(exchanges_config) -> dict:
  function get_compatibility_result (line 1271) | def get_compatibility_result(exchange_name, auth_success, compatible_acc...
  function _check_account_with_other_exchange_type_if_possible (line 1285) | async def _check_account_with_other_exchange_type_if_possible(
  function _fetch_is_compatible_account (line 1308) | async def _fetch_is_compatible_account(exchange_name, to_check_config,
  function are_compatible_accounts (line 1346) | def are_compatible_accounts(exchange_details: dict) -> dict:
  function _is_possible_exchange_config (line 1396) | def _is_possible_exchange_config(exchange_config):
  function _is_real_exchange_value (line 1405) | def _is_real_exchange_value(value):
  function get_current_exchange (line 1412) | def get_current_exchange():
  function get_sandbox_exchanges (line 1419) | def get_sandbox_exchanges() -> list:
  function get_distribution (line 1427) | def get_distribution() -> octobot_enums.OctoBotDistribution:
  function change_reference_market_on_config_currencies (line 1431) | def change_reference_market_on_config_currencies(old_base_currency: str,...
  function _change_base (line 1454) | def _change_base(pair, new_quote_currency):
  function send_command_to_activated_tentacles (line 1460) | def send_command_to_activated_tentacles(command, wait_for_processing=True):
  function send_command_to_tentacles (line 1469) | def send_command_to_tentacles(command, tentacle_names: list, wait_for_pr...
  function reload_scripts (line 1482) | def reload_scripts():
  function reload_activated_tentacles_config (line 1491) | def reload_activated_tentacles_config():
  function reload_tentacle_config (line 1500) | def reload_tentacle_config(tentacle_name):
  function update_config_currencies (line 1509) | def update_config_currencies(currencies: dict, replace: bool=False):
  function get_config_required_candles_count (line 1548) | def get_config_required_candles_count(exchange_manager):
  function get_live_trading_enabled_exchange_managers (line 1552) | def get_live_trading_enabled_exchange_managers():

FILE: Services/Interfaces/web_interface/models/dashboard.py
  function parse_get_symbol (line 34) | def parse_get_symbol(get_symbol):
  function get_value_from_dict_or_string (line 38) | def get_value_from_dict_or_string(data):
  function format_trades (line 45) | def format_trades(dict_trade_history):
  function format_orders (line 86) | def format_orders(order, min_order_time):
  function _remove_invalid_chars (line 114) | def _remove_invalid_chars(string):
  function _get_candles_reply (line 118) | def _get_candles_reply(exchange, exchange_id, symbol, time_frame):
  function _get_first_exchange_identifiers (line 127) | def _get_first_exchange_identifiers(exchange_name=None, trading_exchange...
  function get_first_exchange_data (line 137) | def get_first_exchange_data(exchange_name=None, trading_exchange_only=Fa...
  function get_watched_symbol_data (line 141) | def get_watched_symbol_data(symbol):
  function _get_default_time_frame (line 163) | def _get_default_time_frame(exchange_name, exchange_id):
  function _is_symbol_data_available (line 173) | def _is_symbol_data_available(exchange_manager, symbol):
  function get_startup_messages (line 177) | def get_startup_messages():
  function get_first_symbol_data (line 181) | def get_first_symbol_data():
  function _create_candles_data (line 191) | def _create_candles_data(exchange_manager, symbol, time_frame, historica...
  function _ensure_time_frame (line 276) | def _ensure_time_frame(time_frame: str):
  function get_currency_price_graph_update (line 285) | def get_currency_price_graph_update(exchange_id, symbol, time_frame, lis...

FILE: Services/Interfaces/web_interface/models/distributions/market_making/configuration.py
  function save_market_making_configuration (line 37) | def save_market_making_configuration(
  function get_market_making_services (line 56) | def get_market_making_services() -> dict:
  function _save_user_config (line 64) | def _save_user_config(
  function _filter_0_values (line 161) | def _filter_0_values(elements: dict) -> dict:
  function _save_tentacle_config (line 169) | def _save_tentacle_config(
  function _get_logger (line 185) | def _get_logger():

FILE: Services/Interfaces/web_interface/models/dsl.py
  function get_dsl_keywords_docs (line 22) | def get_dsl_keywords_docs() -> list[dsl_interpreter.OperatorDocs]:

FILE: Services/Interfaces/web_interface/models/interface_settings.py
  function get_watched_symbols (line 24) | def get_watched_symbols():
  function add_watched_symbol (line 33) | def add_watched_symbol(symbol):
  function remove_watched_symbol (line 41) | def remove_watched_symbol(symbol):
  function set_color_mode (line 50) | def set_color_mode(color_mode: str):
  function set_display_announcement (line 60) | def set_display_announcement(key: str, display: bool):
  function get_display_announcement (line 72) | def get_display_announcement(key: str) -> bool:
  function get_color_mode (line 81) | def get_color_mode() -> web_enums.ColorModes:
  function get_display_timeframe (line 87) | def get_display_timeframe():
  function get_display_orders (line 94) | def get_display_orders():
  function set_display_timeframe (line 98) | def set_display_timeframe(time_frame):
  function set_display_orders (line 105) | def set_display_orders(display_orders):
  function get_web_interface_config (line 112) | def get_web_interface_config():
  function _save_edition (line 119) | def _save_edition():
  function reload_config (line 129) | def reload_config():
  function get_web_interface (line 133) | def get_web_interface():

FILE: Services/Interfaces/web_interface/models/json_schemas.py
  function get_json_simulated_portfolio (line 54) | def get_json_simulated_portfolio(user_config):
  function json_simulated_portfolio_to_config (line 65) | def json_simulated_portfolio_to_config(json_portfolio_config: list[dict]...
  function get_json_trading_simulator_config (line 111) | def get_json_trading_simulator_config(user_config: dict) -> dict:
  function get_json_exchanges_schema (line 119) | def get_json_exchanges_schema(exchanges: list[str]) -> dict:
  function get_json_exchange_config (line 161) | def get_json_exchange_config(user_config: dict):
  function json_exchange_config_to_config (line 172) | def json_exchange_config_to_config(json_exchanges_config: list[dict], en...
  function _get_exchange_config_from_json (line 178) | def _get_exchange_config_from_json(json_exchange_config: dict, enabled: ...

FILE: Services/Interfaces/web_interface/models/logs.py
  function export_logs (line 23) | def export_logs(export_path):

FILE: Services/Interfaces/web_interface/models/medias.py
  function _is_valid_path (line 23) | def _is_valid_path(path, header):
  function is_valid_tentacle_image_path (line 27) | def is_valid_tentacle_image_path(path):
  function is_valid_profile_image_path (line 32) | def is_valid_profile_image_path(path):
  function is_valid_audio_path (line 37) | def is_valid_audio_path(path):

FILE: Services/Interfaces/web_interface/models/profiles.py
  function get_current_profile (line 39) | def get_current_profile():
  function duplicate_profile (line 43) | def duplicate_profile(profile_id):
  function convert_to_live_profile (line 51) | def convert_to_live_profile(profile_id):
  function select_profile (line 57) | def select_profile(profile_id):
  function _select_and_save (line 61) | def _select_and_save(config, profile_id):
  function _update_edited_tentacles_config (line 67) | def _update_edited_tentacles_config(config):
  function get_profile (line 72) | def get_profile(profile_id):
  function get_tentacles_setup_config_from_profile_id (line 76) | def get_tentacles_setup_config_from_profile_id(profile_id):
  function get_tentacles_setup_config_from_profile (line 80) | def get_tentacles_setup_config_from_profile(profile):
  function get_profiles (line 86) | def get_profiles(profile_type: commons_enums.ProfileType = None):
  function _get_profile_setup_config (line 94) | def _get_profile_setup_config(profile, reloading_profile):
  function get_profiles_tentacles_details (line 110) | def get_profiles_tentacles_details(profiles_list):
  function update_profile (line 131) | def update_profile(profile_id, json_profile_desc, json_profile_content=N...
  function remove_profile (line 150) | def remove_profile(profile_id):
  function export_profile (line 162) | def export_profile(profile_id, export_path) -> str:
  function import_profile (line 166) | def import_profile(profile_path, name, profile_url=None):
  function import_strategy_as_profile (line 172) | def import_strategy_as_profile(authenticator, strategy: community.Strate...
  function download_and_import_profile (line 197) | def download_and_import_profile(profile_url):
  function get_profile_name (line 209) | def get_profile_name(profile_id) -> str:
  function get_forced_profile (line 213) | def get_forced_profile() -> profiles.Profile:
  function is_real_trading (line 229) | def is_real_trading(profile):

FILE: Services/Interfaces/web_interface/models/strategy_optimizer.py
  function get_strategies_list (line 34) | def get_strategies_list(trading_mode):
  function get_time_frames_list (line 41) | def get_time_frames_list(strategy_name):
  function get_evaluators_list (line 53) | def get_evaluators_list(strategy_name):
  function get_risks_list (line 65) | def get_risks_list():
  function cancel_optimizer (line 69) | def cancel_optimizer():
  function start_optimizer (line 78) | def start_optimizer(strategy, time_frames, evaluators, risks):
  function get_optimizer_results (line 110) | def get_optimizer_results():
  function get_optimizer_report (line 119) | def get_optimizer_report():
  function get_current_run_params (line 127) | def get_current_run_params():
  function get_optimizer_status (line 147) | def get_optimizer_status():

FILE: Services/Interfaces/web_interface/models/tentacles.py
  function get_tentacles_packages (line 27) | def get_tentacles_packages():
  function call_tentacle_manager (line 32) | def call_tentacle_manager(coro, *args, **kwargs):
  function _add_version_to_tentacles_package_path (line 36) | def _add_version_to_tentacles_package_path(path_or_url, version):
  function get_official_tentacles_url (line 40) | def get_official_tentacles_url(use_beta_tentacles) -> str:
  function install_packages (line 46) | def install_packages(path_or_url=None, version=None, authenticator=None):
  function update_packages (line 72) | def update_packages(authenticator=None):
  function reset_packages (line 90) | def reset_packages():
  function update_modules (line 99) | def update_modules(modules):
  function uninstall_modules (line 121) | def uninstall_modules(modules):
  function get_tentacles (line 129) | def get_tentacles():

FILE: Services/Interfaces/web_interface/models/trading.py
  function ensure_valid_exchange_id (line 36) | def ensure_valid_exchange_id(exchange_id) -> str:
  function get_exchange_watched_time_frames (line 43) | def get_exchange_watched_time_frames(exchange_id):
  function get_all_watched_time_frames (line 51) | def get_all_watched_time_frames():
  function get_initializing_currencies_prices_set (line 59) | def get_initializing_currencies_prices_set(fetch_timeout):
  function get_evaluation (line 70) | def get_evaluation(symbol, exchange_name, exchange_id):
  function get_exchanges_load (line 87) | def get_exchanges_load():
  function _add_exchange_portfolio (line 100) | def _add_exchange_portfolio(portfolio, exchange, holdings_per_symbol):
  function get_exchange_holdings_per_symbol (line 124) | def get_exchange_holdings_per_symbol():
  function get_symbols_values (line 132) | def get_symbols_values(symbols, has_real_trader, has_simulated_trader):
  function _get_exchange_historical_portfolio (line 141) | def _get_exchange_historical_portfolio(exchange_manager, currency, time_...
  function _merge_all_exchanges_historical_portfolio (line 155) | def _merge_all_exchanges_historical_portfolio(currency, time_frame, from...
  function get_portfolio_historical_values (line 177) | def get_portfolio_historical_values(currency, time_frame=None, from_time...
  function _get_valid_pnl_history (line 187) | def _get_valid_pnl_history(exchange_manager, quote, symbol, since):
  function _is_valid_pnl (line 200) | def _is_valid_pnl(pnl):
  function _get_pnl_history (line 207) | def _get_pnl_history(exchange, quote, symbol, since):
  function get_pnl_history_symbols (line 223) | def get_pnl_history_symbols(exchange=None, quote=None, symbol=None, sinc...
  function _convert_timestamp (line 232) | def _convert_timestamp(timestamp):
  function get_pnl_history (line 236) | def get_pnl_history(exchange=None, quote=None, symbol=None, since=None, ...
  function _get_dumped_data (line 332) | def _get_dumped_data(real, simulated, dump_func):
  function _dump_order (line 369) | def _dump_order(order, is_simulated):
  function get_all_orders_data (line 395) | def get_all_orders_data():
  function _convert_amount (line 399) | def _convert_amount(exchange_manager, amount, currency):
  function _dump_trade (line 406) | def _dump_trade(trade, is_simulated):
  function get_all_trades_data (line 430) | def get_all_trades_data(independent_backtesting=None):
  function _get_market (line 434) | def _get_market(symbol_str):
  function _dump_position (line 439) | def _dump_position(position, is_simulated):
  function get_all_positions_data (line 460) | def get_all_positions_data():
  function clear_exchanges_orders_history (line 469) | def clear_exchanges_orders_history(simulated_only=False):
  function clear_exchanges_trades_history (line 474) | def clear_exchanges_trades_history(simulated_only=False):
  function clear_exchanges_transactions_history (line 479) | def clear_exchanges_transactions_history(simulated_only=False):
  function clear_exchanges_portfolio_history (line 484) | def clear_exchanges_portfolio_history(simulated_only=False, simulated_po...
  function _async_run_on_exchange_ids (line 496) | async def _async_run_on_exchange_ids(coro, exchange_ids, simulated_only,...
  function _run_on_exchange_ids (line 503) | def _run_on_exchange_ids(coro, simulated_only=False, **kwargs):
  function _sync_run_on_exchange_ids (line 509) | def _sync_run_on_exchange_ids(func, simulated_only=False, **kwargs):

FILE: Services/Interfaces/web_interface/models/web_interface_tab.py
  class WebInterfaceTab (line 18) | class WebInterfaceTab:
    method __init__ (line 19) | def __init__(
    method is_available (line 28) | def is_available(self, has_open_source_package):

FILE: Services/Interfaces/web_interface/plugins/abstract_plugin.py
  class AbstractWebInterfacePlugin (line 26) | class AbstractWebInterfacePlugin(tentacles_management.AbstractTentacle):
    method __init__ (line 35) | def __init__(self, name, url_prefix, plugin_folder, template_folder, s...
    method get_name (line 47) | def get_name(cls):
    method register_routes (line 50) | def register_routes(self):
    method get_tabs (line 53) | def get_tabs(self):
    method init_user_inputs_from_class (line 61) | def init_user_inputs_from_class(cls, inputs: dict) -> None:
    method is_configurable (line 67) | def is_configurable(cls):
    method blueprint_factory (line 73) | def blueprint_factory(self):
    method factory (line 85) | def factory(cls, **kwargs):
    method register (line 97) | def register(self, server_instance):
    method get_tentacle_config (line 104) | def get_tentacle_config(cls, tentacles_setup_config=None):
    method __str__ (line 109) | def __str__(self):

FILE: Services/Interfaces/web_interface/plugins/plugin_management.py
  function register_all_plugins (line 23) | def register_all_plugins(server_instance, already_registered_plugins, **...
  function _get_all_plugins (line 51) | def _get_all_plugins() -> list:

FILE: Services/Interfaces/web_interface/security.py
  function register_responses_extra_header (line 25) | def register_responses_extra_header(flask_app, high_security_level):
  function _prepare_response_extra_headers (line 44) | def _prepare_response_extra_headers(include_security_headers):
  function is_safe_url (line 68) | def is_safe_url(target):

FILE: Services/Interfaces/web_interface/static/js/common/backtesting_util.js
  function start_backtesting (line 20) | function start_backtesting(request, update_url, success_callback=null){
  function start_success_callback (line 25) | function start_success_callback(updated_data, update_url, dom_root_eleme...
  function start_error_callback (line 30) | function start_error_callback(updated_data, update_url, dom_root_element...
  function lock_interface (line 36) | function lock_interface(lock=true){
  function load_report (line 48) | function load_report(report, should_alert=False) {
  function add_graphs (line 157) | function add_graphs(chart_identifiers){
  function updateBacktestingProgress (line 179) | function updateBacktestingProgress(progress){
  function refreshBacktestingStatus (line 183) | function refreshBacktestingStatus(){
  function init_backtesting_status_websocket (line 187) | function init_backtesting_status_websocket(){
  function _handle_backtesting (line 194) | function _handle_backtesting(backtesting_status_data){

FILE: Services/Interfaces/web_interface/static/js/common/bot_connection.js
  function init_status_websocket (line 19) | function init_status_websocket(){
  function manage_alert (line 38) | function manage_alert(data){
  function handle_route_button (line 69) | function handle_route_button(){
  function send_and_interpret_bot_update (line 96) | function send_and_interpret_bot_update(updated_data, update_url, dom_roo...
  function isBotDisconnected (line 145) | function isBotDisconnected(){
  function checkDisconnected (line 149) | async function checkDisconnected(){
  function register_notification_callback (line 158) | function register_notification_callback(callback){
  function registerReconnectedCallback (line 162) | function registerReconnectedCallback(callback){

FILE: Services/Interfaces/web_interface/static/js/common/candlesticks.js
  function get_symbol_price_graph (line 19) | function get_symbol_price_graph(element_id, exchange_id, exchange_name, ...
  function get_first_symbol_price_graph (line 71) | function get_first_symbol_price_graph(element_id, in_backtesting_mode=fa...
  function get_watched_symbol_price_graph (line 95) | function get_watched_symbol_price_graph(element, callback=undefined, no_...
  function create_candlesticks (line 127) | function create_candlesticks(candles){
  function create_volume (line 150) | function create_volume(candles){
  function create_trades (line 184) | function create_trades(trades, trader){
  function create_orders (line 233) | function create_orders(orders, trader, firstTime, lastTime){
  function update_trades (line 260) | function update_trades(trades, trader_name, reference_trades){
  function update_last_candle (line 279) | function update_last_candle(to_update_candles, to_update_vols, new_candl...
  function create_layout (line 290) | function create_layout(graph_title){
  function push_new_candle (line 331) | function push_new_candle(price_trace, volume_trace, candles, candle_inde...
  function create_or_update_candlestick_graph (line 343) | function create_or_update_candlestick_graph(element_id, symbol_price_dat...

FILE: Services/Interfaces/web_interface/static/js/common/custom_elements.js
  function create_circular_progress_doughnut (line 19) | function create_circular_progress_doughnut(element, label1="% Done", lab...
  function create_doughnut_chart (line 39) | function create_doughnut_chart(element, data, title, displayLegend=true,...
  function create_line_chart (line 94) | function create_line_chart(element, data, title, fontColor='white', upda...
  function create_histogram_chart (line 160) | function create_histogram_chart(element, data, titleY1, titleY2, nameYAx...
  function update_circular_progress_doughnut (line 244) | function update_circular_progress_doughnut(chart, done, remaining){
  function create_bars_chart (line 250) | function create_bars_chart(element, labels, datasets, min_y=0, displayLe...
  function update_bars_chart (line 288) | function update_bars_chart(chart, datasets){

FILE: Services/Interfaces/web_interface/static/js/common/data_collector_util.js
  function lock_collector_ui (line 19) | function lock_collector_ui(lock=true){
  function _refreshDataCollectorStatus (line 31) | function _refreshDataCollectorStatus(socket){
  function updateDataCollectorProgress (line 35) | function updateDataCollectorProgress(current_progress, total_progress){
  function init_data_collector_status_websocket (line 46) | function init_data_collector_status_websocket(){
  function _handle_data_collector_status (line 53) | function _handle_data_collector_status(data_collector_status_data, socket){

FILE: Services/Interfaces/web_interface/static/js/common/dom_updater.js
  function update_badge (line 19) | function update_badge(badge, new_text, new_class){
  function update_list_item (line 32) | function update_list_item(list_item, new_class){
  function update_element_required_marker_and_usability (line 38) | function update_element_required_marker_and_usability(element, display_m...
  function update_element_temporary_look (line 51) | function update_element_temporary_look(element){
  function change_boolean (line 82) | function change_boolean(to_update_element, new_value, new_value_string){
  function update_activated_deactivated_tentacles (line 103) | function update_activated_deactivated_tentacles(root_element, message, e...
  function update_dom (line 131) | function update_dom(root_element, message){
  function create_alert (line 146) | function create_alert(a_level, a_title, a_msg, url="_blank", sound=null){
  function lock_ui (line 171) | function lock_ui(){
  function unlock_ui (line 176) | function unlock_ui(){
  function update_status (line 181) | function update_status(status){
  function register_exit_confirm_function (line 203) | function register_exit_confirm_function(check_function) {
  function remove_exit_confirm_function (line 212) | function remove_exit_confirm_function(){
  function confirm_all_modified_classes (line 218) | function confirm_all_modified_classes(container){
  function toggle_class (line 230) | function toggle_class(elem, class_type, toogle=true){
  function toogle_deck_container_modified (line 238) | function toogle_deck_container_modified(container, modified=true) {
  function toogle_card_modified (line 242) | function toogle_card_modified(card, modified=true) {

FILE: Services/Interfaces/web_interface/static/js/common/exchange_accounts.js
  function register_exchanges_checks (line 18) | function register_exchanges_checks(check_existing_accounts){

FILE: Services/Interfaces/web_interface/static/js/common/feedback.js
  function displayFeedbackForm (line 1) | function displayFeedbackForm(formId, userId, updateUrl) {

FILE: Services/Interfaces/web_interface/static/js/common/json_editor_settings.js
  class OctoBotTheme (line 22) | class OctoBotTheme extends JSONEditor.defaults.themes.bootstrap4 {
    method getButton (line 23) | getButton(text, icon, title) {
    method getCheckbox (line 29) | getCheckbox() {
    method getCheckboxLabel (line 34) | getCheckboxLabel(text) {
    method getFormControl (line 39) | getFormControl(label, input, description) {
    method getIndentedPanel (line 59) | getIndentedPanel () {
  class ConfirmArray (line 79) | class ConfirmArray extends JSONEditor.defaults.editors.array {
    method askConfirmation (line 80) | askConfirmation() {

FILE: Services/Interfaces/web_interface/static/js/common/resources_rendering.js
  function markdown_to_html (line 22) | function markdown_to_html(text) {
  function fetch_images (line 28) | function fetch_images() {
  function handleDefaultImage (line 44) | function handleDefaultImage(element, url){
  function fetchCurrencyIds (line 68) | function fetchCurrencyIds(){
  function handleDefaultImages (line 98) | function handleDefaultImages(){
  function handle_copy_to_clipboard (line 183) | function handle_copy_to_clipboard() {

FILE: Services/Interfaces/web_interface/static/js/common/stepper.js
  function updateProgress (line 19) | function updateProgress(){
  function triggerCallbacksIfAny (line 24) | function triggerCallbacksIfAny(stepId){
  function updateButtonsDisplay (line 30) | function updateButtonsDisplay(){
  function getStep (line 47) | function getStep(stepId){
  function getCurrentStep (line 51) | function getCurrentStep(){
  function getCurrentStepId (line 55) | function getCurrentStepId(){
  function getStepsCount (line 59) | function getStepsCount(){
  function changeStep (line 63) | function changeStep(next){
  function handleStepsButtons (line 76) | function handleStepsButtons(){

FILE: Services/Interfaces/web_interface/static/js/common/tables_display.js
  constant MAX_PRICE_DIGITS (line 1) | const MAX_PRICE_DIGITS = 8;

FILE: Services/Interfaces/web_interface/static/js/common/tracking.js
  function posthog_loaded (line 1) | function posthog_loaded(posthog) {

FILE: Services/Interfaces/web_interface/static/js/common/tutorial.js
  function getWebsiteLink (line 1) | function getWebsiteLink(route, name) {
  function getDocsLink (line 5) | function getDocsLink(route, name) {
  function getExchangesDocsLink (line 9) | function getExchangesDocsLink(route, name) {
  function registerTutorial (line 323) | function registerTutorial(tutorialName, callback){
  function displayLocalTutorial (line 327) | function displayLocalTutorial(tutorialName, afterExitCallback){
  function startTutorialIfNecessary (line 344) | function startTutorialIfNecessary(tutorialName, afterExitCallback=null) {

FILE: Services/Interfaces/web_interface/static/js/common/util.js
  function get_websocket (line 19) | function get_websocket(namespace){
  function getAudioMediaUrl (line 31) | function getAudioMediaUrl(mediaName){
  function setup_editable (line 36) | function setup_editable(){
  function get_color (line 40) | function get_color(index){
  function get_dark_color (line 45) | function get_dark_color(index){
  function handle_editable (line 50) | function handle_editable(){
  function hide_editables (line 58) | function hide_editables(elements){
  function trigger_file_downloader_on_click (line 65) | function trigger_file_downloader_on_click(element){
  function replace_break_line (line 73) | function replace_break_line(str, replacement=""){
  function replace_spaces (line 77) | function replace_spaces(str, replacement=""){
  function get_selected_options (line 81) | function get_selected_options(element){
  function isDefined (line 91) | function isDefined(thing){
  function log (line 95) | function log(...texts){
  function get_events (line 101) | function get_events(elem, event_type){
  function add_event_if_not_already_added (line 109) | function add_event_if_not_already_added(elem, event_type, handler){
  function updateProgressBar (line 115) | function updateProgressBar(elementId, progress){
  function check_has_event_using_handler (line 119) | function check_has_event_using_handler(elem, event_type, handler){
  function generic_request_success_callback (line 130) | function generic_request_success_callback(updated_data, update_url, dom_...
  function generic_request_failure_callback (line 138) | function generic_request_failure_callback(updated_data, update_url, dom_...
  function isMobileDisplay (line 146) | function isMobileDisplay() {
  function isMediumDisplay (line 150) | function isMediumDisplay() {
  function round_digits (line 177) | function round_digits(number, decimals) {
  function handle_numbers (line 186) | function handle_numbers(number) {
  function fix_config_values (line 208) | function fix_config_values(config, schema){
  function getValueChangedFromRef (line 231) | function getValueChangedFromRef(newObject, refObject, allowUndefinedValu...
  function historyGoBack (line 265) | function historyGoBack() {
  function showModalIfAny (line 269) | function showModalIfAny(element){
  function hideModalIfAny (line 275) | function hideModalIfAny(element){
  function unique (line 305) | function unique(array){
  function download_data (line 311) | function download_data(data, filename, content_type="application/json"){
  function display_generic_modal (line 321) | function display_generic_modal(title, content, warning, yes_button_callb...
  function updateInputIfValue (line 344) | function updateInputIfValue(elementId, config, configKey, elementType){
  function randomizeArray (line 361) | function randomizeArray(array) {
  function validateJSONEditor (line 365) | function validateJSONEditor(editor) {
  function getWebsiteUrl (line 377) | function getWebsiteUrl() {
  function getDocsUrl (line 381) | function getDocsUrl() {
  function getExchangesDocsUrl (line 385) | function getExchangesDocsUrl() {
  function paginatedSelect2 (line 389) | function paginatedSelect2(selectElement, options, pageSize){
  function activate_tab (line 436) | function activate_tab(tabElement, nestedNavBar=undefined){
  function selectFirstTab (line 449) | function selectFirstTab(nestedNavBar=undefined){
  function copyToClipBoard (line 464) | function copyToClipBoard(name, value) {
  function sleep (line 475) | async function sleep(milliseconds) {

FILE: Services/Interfaces/web_interface/static/js/components/advanced_matrix.js
  function init_select_filter (line 19) | function init_select_filter(){

FILE: Services/Interfaces/web_interface/static/js/components/backtesting.js
  function get_selected_files (line 21) | function get_selected_files(){
  function handle_backtesting_buttons (line 36) | function handle_backtesting_buttons(){
  function handle_file_selection (line 56) | function handle_file_selection(){
  function check_date_range (line 88) | function check_date_range(){
  function check_date_range_available (line 94) | function check_date_range_available() {
  function handle_date_selection (line 99) | function handle_date_selection(){

FILE: Services/Interfaces/web_interface/static/js/components/commands.js
  function load_commands_metadata (line 19) | function load_commands_metadata() {
  function setNoFeedback (line 41) | function setNoFeedback(feedbackButton){
  function update_metrics_option (line 45) | function update_metrics_option(){
  function update_beta_option (line 58) | function update_beta_option(){
  function update_failure_callback (line 72) | function update_failure_callback(updated_data, update_url, dom_root_elem...

FILE: Services/Interfaces/web_interface/static/js/components/community.js
  function disablePackagesOperations (line 19) | function disablePackagesOperations(should_lock=true){
  function reloadTable (line 24) | function reloadTable(){
  function registerPackagesEvents (line 33) | function registerPackagesEvents(){
  function selectProfile (line 47) | function selectProfile(profileId) {
  function packagesOperationSuccessCallback (line 54) | function packagesOperationSuccessCallback(updated_data, update_url, dom_...
  function packagesOperationErrorCallback (line 64) | function packagesOperationErrorCallback(updated_data, update_url, dom_ro...
  function displayBotSelectorWhenNoSelectedBot (line 69) | function displayBotSelectorWhenNoSelectedBot(){
  function disableBotsSelectAndCreate (line 76) | function disableBotsSelectAndCreate(disabled){
  function initBotsCallbacks (line 81) | function initBotsCallbacks(){
  function botOperationSuccessCallback (line 102) | function botOperationSuccessCallback(updated_data, update_url, dom_root_...
  function botOperationErrorCallback (line 107) | function botOperationErrorCallback(updated_data, update_url, dom_root_el...
  function initLoginSubmit (line 111) | function initLoginSubmit(){

FILE: Services/Interfaces/web_interface/static/js/components/config_tentacle.js
  function apply_evaluator_default_config (line 19) | function apply_evaluator_default_config(element) {
  function handle_apply_evaluator_default_config_success_callback (line 36) | function handle_apply_evaluator_default_config_success_callback(updated_...
  function updateTentacleConfig (line 41) | function updateTentacleConfig(updatedConfig, update_url){
  function factory_reset (line 45) | function factory_reset(update_url){
  function handle_tentacle_config_reset_success_callback (line 49) | function handle_tentacle_config_reset_success_callback(updated_data, upd...
  function handle_tentacle_config_update_success_callback (line 54) | function handle_tentacle_config_update_success_callback(updated_data, up...
  function handle_tentacle_config_update_error_callback (line 59) | function handle_tentacle_config_update_error_callback(updated_data, upda...
  function handleConfigDisplay (line 63) | function handleConfigDisplay(success){
  function get_config_value_changed (line 87) | function get_config_value_changed(element, new_value) {
  function handle_save_buttons_success_callback (line 92) | function handle_save_buttons_success_callback(updated_data, update_url, ...
  function send_command_success_callback (line 97) | function send_command_success_callback(updated_data, update_url, dom_roo...
  function handle_save_button (line 101) | function handle_save_button(){
  function handleUserCommands (line 127) | function handleUserCommands(){
  function handleButtons (line 145) | function handleButtons() {
  function check_date_range (line 182) | function check_date_range(){
  function get_config_key (line 188) | function get_config_key(elem){
  function parse_new_value (line 192) | function parse_new_value(element) {
  function handle_evaluator_configuration_editor (line 196) | function handle_evaluator_configuration_editor(){
  function something_is_unsaved (line 228) | function something_is_unsaved(){
  function get_selected_files (line 238) | function get_selected_files(){
  function canEditConfig (line 243) | function canEditConfig() {
  function _addGridDisplayOptions (line 254) | function _addGridDisplayOptions(schema){
  function initConfigEditor (line 276) | function initConfigEditor(showWaiter) {
  function editorChangeCallback (line 339) | function editorChangeCallback(){
  function addEditorChangeEventCallback (line 347) | function addEditorChangeEventCallback(callback){

FILE: Services/Interfaces/web_interface/static/js/components/configuration.js
  function handle_nested_sidenav (line 22) | function handle_nested_sidenav(){
  function get_tabs_config (line 31) | function get_tabs_config(){
  function handle_reset_buttons (line 36) | function handle_reset_buttons(){
  function handle_remove_buttons (line 42) | function handle_remove_buttons(){
  function handle_buttons (line 58) | function handle_buttons() {
  function check_deck_modifications (line 66) | function check_deck_modifications(deck){
  function handle_add_buttons (line 74) | function handle_add_buttons(){
  function handleEditableAddButtons (line 79) | function handleEditableAddButtons(){
  function handleEditableRenameIfNotAlready (line 92) | function handleEditableRenameIfNotAlready(e, params){
  function registerHandleEditableRenameIfNotAlready (line 113) | function registerHandleEditableRenameIfNotAlready(element, events, handl...
  function handleCardDecksAddButtons (line 123) | function handleCardDecksAddButtons(){
  function handle_special_values (line 229) | function handle_special_values(currentElem){
  function register_edit_events (line 258) | function register_edit_events(){
  function card_edit_handler (line 271) | function card_edit_handler(e, params){
  function something_is_unsaved (line 302) | function something_is_unsaved(){
  function parse_new_value (line 312) | function parse_new_value(element){
  function _save_config (line 354) | function _save_config(element, restart_after_save) {
  function handle_save_buttons (line 396) | function handle_save_buttons(){
  function get_config_key (line 405) | function get_config_key(elem){
  function get_card_config_key (line 409) | function get_card_config_key(card_component, config_type="global_config"){
  function get_deck_container (line 414) | function get_deck_container(elem) {
  function get_card_container (line 418) | function get_card_container(elem) {
  function get_config_value_changed (line 422) | function get_config_value_changed(element, new_value, config_key) {
  function get_value_changed (line 436) | function get_value_changed(new_val, dom_conf_val, config_key){
  function is_different_value (line 454) | function is_different_value(new_val, lower_case_new_val, dom_conf_val){
  function has_element_already_been_updated (line 459) | function has_element_already_been_updated(config_key){
  function has_update_already_been_applied (line 463) | function has_update_already_been_applied(lower_case_val, config_key){
  function handle_save_buttons_success_callback (line 467) | function handle_save_buttons_success_callback(updated_data, update_url, ...
  function apply_evaluator_default_config (line 473) | function apply_evaluator_default_config(element) {
  function handle_apply_evaluator_default_config_success_callback (line 490) | function handle_apply_evaluator_default_config_success_callback(updated_...
  function other_element_activated (line 494) | function other_element_activated(root_element){
  function deactivate_other_elements (line 499) | function deactivate_other_elements(element, root_element) {
  function updateTradingModeSummary (line 510) | function updateTradingModeSummary(selectedElement){
  function updateStrategySelector (line 521) | function updateStrategySelector(required_elements){
  function update_requirement_activation (line 536) | function update_requirement_activation(element) {
  function get_activated_strategies_count (line 556) | function get_activated_strategies_count() {
  function get_activated_trading_mode_min_strategies (line 560) | function get_activated_trading_mode_min_strategies(){
  function check_evaluator_configuration (line 569) | function check_evaluator_configuration() {
  function handle_activation_configuration_editor (line 597) | function handle_activation_configuration_editor(){
  function handle_import_currencies (line 670) | function handle_import_currencies(){
  function handle_export_currencies_button (line 689) | function handle_export_currencies_button(){
  function reset_configuration_element (line 699) | function reset_configuration_element(){
  function updated_validated_updated_global_config (line 704) | function updated_validated_updated_global_config(updated_data){
  function fetch_currencies (line 722) | function fetch_currencies(){

FILE: Services/Interfaces/web_interface/static/js/components/dashboard.js
  function _refresh_profitability (line 28) | function _refresh_profitability(socket) {
  function handle_profitability (line 33) | function handle_profitability(socket) {
  function get_in_backtesting_mode (line 82) | function get_in_backtesting_mode() {
  function init_dashboard_websocket (line 86) | function init_dashboard_websocket() {
  function get_version_upgrade (line 90) | function get_version_upgrade() {
  function handle_graph_update (line 113) | function handle_graph_update() {
  function _find_symbol_details (line 136) | function _find_symbol_details(symbol, exchange_id) {
  function update_graph (line 148) | function update_graph(data, re_update = true) {
  function init_updater (line 175) | function init_updater(exchange_id, symbol, time_frame, elem_id) {
  function enable_default_graph (line 197) | function enable_default_graph(time_frame) {
  function no_data_for_graph (line 204) | function no_data_for_graph(element_id) {
  function init_graphs (line 212) | function init_graphs() {
  function registerGraphUpdateCallback (line 279) | function registerGraphUpdateCallback(callback) {

FILE: Services/Interfaces/web_interface/static/js/components/data_collector.js
  function handle_data_files_buttons (line 19) | function handle_data_files_buttons(){
  function handle_file_selection (line 29) | function handle_file_selection(){
  function delete_success_callback (line 37) | function delete_success_callback(updated_data, update_url, dom_root_elem...
  function delete_error_callback (line 44) | function delete_error_callback(updated_data, update_url, dom_root_elemen...
  function reload_table (line 48) | function reload_table(){
  function start_collector (line 64) | function start_collector(){
  function stop_collector (line 76) | function stop_collector(){
  function collector_success_callback (line 81) | function collector_success_callback(updated_data, update_url, dom_root_e...
  function collector_error_callback (line 86) | function collector_error_callback(updated_data, update_url, dom_root_ele...
  function display_alert (line 91) | function display_alert(success, message){
  function update_symbol_list (line 99) | function update_symbol_list(url, exchange){
  function update_available_timeframes_list (line 112) | function update_available_timeframes_list(url, exchange){
  function check_date_input (line 125) | function check_date_input(){
  function is_full_candle_history_exchanges (line 142) | function is_full_candle_history_exchanges(){
  function handleSelects (line 151) | function handleSelects(){
  function createSelect2 (line 201) | function createSelect2(){

FILE: Services/Interfaces/web_interface/static/js/components/evaluator_configuration.js
  function get_tabs_config (line 19) | function get_tabs_config(){
  function handle_reset_buttons (line 23) | function handle_reset_buttons(){
  function something_is_unsaved (line 29) | function something_is_unsaved(){
  function parse_new_value (line 39) | function parse_new_value(element){
  function _save_eval_config (line 81) | function _save_eval_config(element, restart_after_save){
  function handle_save_buttons (line 110) | function handle_save_buttons(){
  function get_config_key (line 119) | function get_config_key(elem){
  function get_config_value_changed (line 123) | function get_config_value_changed(element, new_value, config_key) {
  function get_value_changed (line 136) | function get_value_changed(new_val, dom_conf_val, config_key){
  function handle_save_buttons_success_callback (line 148) | function handle_save_buttons_success_callback(updated_data, update_url, ...
  function handle_evaluator_configuration_editor (line 153) | function handle_evaluator_configuration_editor(){
  function reset_configuration_element (line 193) | function reset_configuration_element(){

FILE: Services/Interfaces/web_interface/static/js/components/logs.js
  function handleLogsExporter (line 1) | function handleLogsExporter(){

FILE: Services/Interfaces/web_interface/static/js/components/market_status.js
  function get_in_backtesting_mode (line 20) | function get_in_backtesting_mode() {
  function init_update_handler (line 25) | function init_update_handler(){
  function schedule_update (line 42) | function schedule_update(){
  function update_graph (line 49) | function update_graph(exchange, update=false, data=undefined, re_update=...
  function change_time_frame (line 78) | function change_time_frame(new_time_frame) {

FILE: Services/Interfaces/web_interface/static/js/components/navbar.js
  function trigger_trader_state (line 19) | function trigger_trader_state(element) {
  function displayTradingStateModal (line 58) | function displayTradingStateModal() {
  function hideTradingStateModal (line 62) | function hideTradingStateModal() {

FILE: Services/Interfaces/web_interface/static/js/components/profile_management.js
  function handleProfileActivator (line 1) | function handleProfileActivator(){
  function onProfileEdit (line 11) | function onProfileEdit(isEditing, profileSave){
  function handleProfileEditor (line 17) | function handleProfileEditor(){
  function saveCurrentProfileSuccessCallback (line 52) | function saveCurrentProfileSuccessCallback(updated_data, update_url, dom...
  function saveCurrentProfileFailureCallback (line 63) | function saveCurrentProfileFailureCallback(updated_data, update_url, dom...
  function handleProfileCreator (line 68) | function handleProfileCreator(){
  function profileActionSuccessCallback (line 78) | function profileActionSuccessCallback(updated_data, update_url, dom_root...
  function profileActionFailureCallback (line 83) | function profileActionFailureCallback(updated_data, update_url, dom_root...
  function handleProfileImporter (line 87) | function handleProfileImporter(){
  function handleProfileDownloader (line 101) | function handleProfileDownloader(){
  function handleProfileExporter (line 114) | function handleProfileExporter(){
  function selectCurrentProfile (line 118) | function selectCurrentProfile(profileNameDisplay){
  function handleProfileSelector (line 124) | function handleProfileSelector(){
  function handleProfileRemover (line 134) | function handleProfileRemover(){

FILE: Services/Interfaces/web_interface/static/js/components/strategy_optimizer.js
  function recompute_nb_iterations (line 19) | function recompute_nb_iterations(){
  function check_disabled (line 26) | function check_disabled(lock=false){
  function start_optimizer (line 38) | function start_optimizer(source){
  function lock_inputs (line 51) | function lock_inputs(lock=true){
  function start_optimizer_success_callback (line 77) | function start_optimizer_success_callback(data, update_url, source, msg,...
  function start_optimizer_error_callback (line 82) | function start_optimizer_error_callback(data, update_url, source, result...
  function populate_select (line 88) | function populate_select(element, options){
  function update_strategy_params (line 99) | function update_strategy_params(url, strategy){
  function updateOptimizerProgress (line 107) | function updateOptimizerProgress(progress, overall_progress){
  function check_optimizer_state (line 118) | function check_optimizer_state(socket){
  function handle_optimizer_state_update (line 122) | function handle_optimizer_state_update(data){
  function init_websocket (line 279) | function init_websocket(){
  function init_data_tables_and_refreshers (line 287) | function init_data_tables_and_refreshers(){
  function register_events (line 322) | function register_events(){

FILE: Services/Interfaces/web_interface/static/js/components/tentacles_configuration.js
  function register_and_install_package (line 19) | function register_and_install_package(){
  function disable_packages_operations (line 31) | function disable_packages_operations(should_lock=true){
  function update (line 46) | function update(module){
  function uninstall (line 50) | function uninstall(module){
  function perform_modules_operation (line 56) | function perform_modules_operation(modules, operation){
  function perform_packages_operation (line 63) | function perform_packages_operation(source){
  function modules_operation_success_callback (line 70) | function modules_operation_success_callback(updated_data, update_url, do...
  function modules_operation_error_callback (line 82) | function modules_operation_error_callback(updated_data, update_url, dom_...
  function packages_operation_success_callback (line 94) | function packages_operation_success_callback(updated_data, update_url, d...
  function packages_operation_error_callback (line 103) | function packages_operation_error_callback(updated_data, update_url, dom...
  function post_package_action_success_callback (line 112) | function post_package_action_success_callback(updated_data, update_url, ...
  function post_package_action_error_callback (line 126) | function post_package_action_error_callback(updated_data, update_url, do...
  function get_selected_modules (line 136) | function get_selected_modules(){
  function handle_tentacles_buttons (line 144) | function handle_tentacles_buttons(){
  function disable_select_action_buttons (line 176) | function disable_select_action_buttons(){

FILE: Services/Interfaces/web_interface/static/js/components/trading.js
  function debouncedReloadDisplay (line 279) | function debouncedReloadDisplay(){

FILE: Services/Interfaces/web_interface/tests/__init__.py
  function _init_bot (line 53) | async def _init_bot(distribution: octobot.enums.OctoBotDistribution):
  function _start_web_interface (line 86) | def _start_web_interface(interface):
  function get_web_interface (line 92) | async def get_web_interface(require_password: bool, distribution: octobo...
  function check_page_no_login_redirect (line 119) | async def check_page_no_login_redirect(url, session):
  function check_page_login_redirect (line 134) | async def check_page_login_redirect(url, session):
  function get_plugins_routes (line 144) | def get_plugins_routes(web_interface_instance):
  function get_plugin_routes (line 155) | def get_plugin_routes(app, plugin, all_rules=None):
  function _force_validate_on_submit (line 163) | def _force_validate_on_submit(*_):
  function login_user_on_session (line 167) | async def login_user_on_session(session):
  function get_all_plugin_rules (line 178) | def get_all_plugin_rules(app, plugin_class, black_list):
  function _has_no_empty_params (line 188) | def _has_no_empty_params(rule):

FILE: Services/Interfaces/web_interface/tests/distribution_tester.py
  class AbstractDistributionTester (line 26) | class AbstractDistributionTester:
    method test_browse_all_pages_no_required_password (line 34) | async def test_browse_all_pages_no_required_password(self):
    method _inner_test_browse_all_pages_no_required_password (line 38) | async def _inner_test_browse_all_pages_no_required_password(self, blac...
    method test_browse_all_pages_required_password_without_login (line 46) | async def test_browse_all_pages_required_password_without_login(self):
    method _inner_test_browse_all_pages_required_password_without_login (line 49) | async def _inner_test_browse_all_pages_required_password_without_login...
    method test_browse_all_pages_required_password_with_login (line 57) | async def test_browse_all_pages_required_password_with_login(self):
    method inner_test_browse_all_pages_required_password_with_login (line 60) | async def inner_test_browse_all_pages_required_password_with_login(
    method test_logout (line 78) | async def test_logout(self):
    method _get_all_native_rules (line 92) | def _get_all_native_rules(self, web_interface_instance, black_list=None):
    method _get_rule_url (line 107) | def _get_rule_url(self, rule: str):
  function _has_no_empty_params (line 115) | def _has_no_empty_params(rule):

FILE: Services/Interfaces/web_interface/tests/distributions/test_default.py
  class TestDefaultDistribution (line 34) | class TestDefaultDistribution(distribution_tester.AbstractDistributionTe...
    method test_browse_all_pages_no_required_password (line 43) | async def test_browse_all_pages_no_required_password(self):
    method test_browse_all_pages_required_password_without_login (line 46) | async def test_browse_all_pages_required_password_without_login(self):
    method test_browse_all_pages_required_password_with_login (line 49) | async def test_browse_all_pages_required_password_with_login(self):

FILE: Services/Interfaces/web_interface/tests/distributions/test_market_making.py
  class TestMarketMakingDistributionPlugin (line 32) | class TestMarketMakingDistributionPlugin(distribution_tester.AbstractDis...
    method test_browse_all_pages_no_required_password (line 41) | async def test_browse_all_pages_no_required_password(self):
    method test_browse_all_pages_required_password_without_login (line 44) | async def test_browse_all_pages_required_password_without_login(self):
    method test_browse_all_pages_required_password_with_login (line 47) | async def test_browse_all_pages_required_password_with_login(self):

FILE: Services/Interfaces/web_interface/tests/plugin_tester.py
  class AbstractPluginTester (line 23) | class AbstractPluginTester:
    method test_browse_all_pages_no_required_password (line 29) | async def test_browse_all_pages_no_required_password(self):
    method test_browse_all_pages_required_password_without_login (line 38) | async def test_browse_all_pages_required_password_without_login(self):
    method test_browse_all_pages_required_password_with_login (line 47) | async def test_browse_all_pages_required_password_with_login(self):
    method _get_rules (line 65) | def _get_rules(self, web_interface_instance):

FILE: Services/Interfaces/web_interface/util/browser_util.py
  function open_in_background_browser (line 21) | def open_in_background_browser(url):

FILE: Services/Interfaces/web_interface/util/flask_util.py
  function get_rest_reply (line 20) | def get_rest_reply(json_message, code=200, content_type="application/jso...

FILE: Services/Interfaces/web_interface/web.py
  class WebInterface (line 47) | class WebInterface(services_interfaces.AbstractWebInterface):
    method __init__ (line 64) | def __init__(self, config):
    method register_new_exchange_impl (line 87) | async def register_new_exchange_impl(self, exchange_id):
    method reload_config (line 91) | def reload_config(self, tentacles_setup_config=None):
    method _init_web_settings (line 96) | def _init_web_settings(self):
    method _web_trades_callback (line 133) | async def _web_trades_callback(exchange: str, exchange_id: str, crypto...
    method _web_orders_callback (line 141) | async def _web_orders_callback(exchange: str, exchange_id: str, crypto...
    method _web_ohlcv_empty_callback (line 146) | async def _web_ohlcv_empty_callback(
    method _register_on_channels (line 156) | async def _register_on_channels(self, exchange_id):
    method init_flask_plugins_and_config (line 165) | def init_flask_plugins_and_config(self, server_instance):
    method _handle_login (line 195) | def _handle_login(self, server_instance):
    method set_requires_password (line 199) | def set_requires_password(self, requires_password):
    method _register_routes (line 203) | def _register_routes(self, server_instance, distribution: octobot.enum...
    method _prepare_websocket (line 212) | def _prepare_websocket(self, server_instance):
    method _async_run (line 230) | async def _async_run(self) -> bool:
    method _open_web_interface_on_browser (line 265) | def _open_web_interface_on_browser(self):
    method _inner_start (line 273) | async def _inner_start(self):
    method stop (line 276) | async def stop(self):

FILE: Services/Interfaces/web_interface/websockets/abstract_websocket_namespace_notifier.py
  class AbstractWebSocketNamespaceNotifier (line 25) | class AbstractWebSocketNamespaceNotifier(flask_socketio.Namespace, web_i...
    method __init__ (line 27) | def __init__(self, namespace=None):
    method all_clients_send_notifications (line 34) | def all_clients_send_notifications(self, **kwargs) -> bool:
    method on_connect (line 37) | def on_connect(self):
    method on_disconnect (line 40) | def on_disconnect(self, reason):
    method _has_clients (line 44) | def _has_clients(self):
  function websocket_with_login_required_when_activated (line 48) | def websocket_with_login_required_when_activated(func):

FILE: Services/Interfaces/web_interface/websockets/backtesting.py
  class BacktestingNamespace (line 24) | class BacktestingNamespace(websockets.AbstractWebSocketNamespaceNotifier):
    method _get_backtesting_status (line 27) | def _get_backtesting_status():
    method on_backtesting_status (line 32) | def on_backtesting_status(self):
    method all_clients_send_notifications (line 35) | def all_clients_send_notifications(self, **kwargs) -> bool:
    method on_connect (line 45) | def on_connect(self):

FILE: Services/Interfaces/web_interface/websockets/dashboard.py
  class DashboardNamespace (line 28) | class DashboardNamespace(websockets.AbstractWebSocketNamespaceNotifier):
    method _get_profitability (line 31) | def _get_profitability():
    method _format_new_data (line 59) | def _format_new_data(exchange_id=None, trades=None, order=None, symbol...
    method on_profitability (line 70) | def on_profitability(self):
    method all_clients_send_notifications (line 73) | def all_clients_send_notifications(self, **kwargs) -> bool:
    method on_candle_graph_update (line 87) | def on_candle_graph_update(self, data):
    method on_connect (line 103) | def on_connect(self):

FILE: Services/Interfaces/web_interface/websockets/data_collector.py
  class DataCollectorNamespace (line 24) | class DataCollectorNamespace(websockets.AbstractWebSocketNamespaceNotifi...
    method _get_data_collector_status (line 27) | def _get_data_collector_status():
    method on_data_collector_status (line 32) | def on_data_collector_status(self):
    method all_clients_send_notifications (line 35) | def all_clients_send_notifications(self, **kwargs) -> bool:
    method on_connect (line 45) | def on_connect(self):

FILE: Services/Interfaces/web_interface/websockets/notifications.py
  class NotificationsNamespace (line 24) | class NotificationsNamespace(websockets.AbstractWebSocketNamespaceNotifi...
    method _get_update_data (line 27) | def _get_update_data():
    method _client_context_send_notifications (line 33) | def _client_context_send_notifications(self):
    method all_clients_send_notifications (line 36) | def all_clients_send_notifications(self, **kwargs) -> bool:
    method on_connect (line 46) | def on_connect(self):

FILE: Services/Interfaces/web_interface/websockets/strategy_optimizer.py
  class StrategyOptimizerNamespace (line 24) | class StrategyOptimizerNamespace(websockets.AbstractWebSocketNamespaceNo...
    method _get_strategy_optimizer_status (line 27) | def _get_strategy_optimizer_status():
    method on_strategy_optimizer_status (line 38) | def on_strategy_optimizer_status(self):
    method all_clients_send_notifications (line 41) | def all_clients_send_notifications(self, **kwargs) -> bool:
    method on_connect (line 52) | def on_connect(self):

FILE: Services/Notifiers/telegram_notifier/telegram.py
  class TelegramNotifier (line 22) | class TelegramNotifier(notifier.AbstractNotifier):
    method _handle_notification (line 27) | async def _handle_notification(self, notification: notification.Notifi...
    method _send_message (line 32) | async def _send_message(self, notification, text, use_markdown):
    method _get_message_text (line 51) | def _get_message_text(notification):

FILE: Services/Notifiers/twitter_notifier/twitter.py
  class Twi
Condensed preview — 1032 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,303K chars).
[
  {
    "path": ".coveragerc",
    "chars": 31,
    "preview": "[run]\ninclude =\n    tentacles/\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 8153,
    "preview": "name: OctoBot-Tentacles-CI\non:\n  push:\n    branches:\n      - 'master'\n      - 'dev'\n      - 'beta'\n    tags:\n      - '*'"
  },
  {
    "path": ".gitignore",
    "chars": 1906,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": "Automation/actions/cancel_open_order_action/__init__.py",
    "chars": 48,
    "preview": "from .cancel_open_orders import CancelOpenOrders"
  },
  {
    "path": "Automation/actions/cancel_open_order_action/cancel_open_orders.py",
    "chars": 1633,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/actions/cancel_open_order_action/metadata.json",
    "chars": 142,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"CancelOpenOrders\"],\n  \"tentacl"
  },
  {
    "path": "Automation/actions/sell_all_currencies_action/__init__.py",
    "chars": 50,
    "preview": "from .sell_all_currencies import SellAllCurrencies"
  },
  {
    "path": "Automation/actions/sell_all_currencies_action/metadata.json",
    "chars": 143,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"SellAllCurrencies\"],\n  \"tentac"
  },
  {
    "path": "Automation/actions/sell_all_currencies_action/sell_all_currencies.py",
    "chars": 1664,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/actions/send_notification_action/__init__.py",
    "chars": 47,
    "preview": "from .send_notification import SendNotification"
  },
  {
    "path": "Automation/actions/send_notification_action/metadata.json",
    "chars": 142,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"SendNotification\"],\n  \"tentacl"
  },
  {
    "path": "Automation/actions/send_notification_action/send_notification.py",
    "chars": 2248,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/actions/stop_trading_action/__init__.py",
    "chars": 37,
    "preview": "from .stop_trading import StopTrading"
  },
  {
    "path": "Automation/actions/stop_trading_action/metadata.json",
    "chars": 137,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"StopTrading\"],\n  \"tentacles-re"
  },
  {
    "path": "Automation/actions/stop_trading_action/stop_trading.py",
    "chars": 1640,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/conditions/no_condition_condition/__init__.py",
    "chars": 37,
    "preview": "from .no_condition import NoCondition"
  },
  {
    "path": "Automation/conditions/no_condition_condition/metadata.json",
    "chars": 137,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"NoCondition\"],\n  \"tentacles-re"
  },
  {
    "path": "Automation/conditions/no_condition_condition/no_condition.py",
    "chars": 1299,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/conditions/scripted_condition/__init__.py",
    "chars": 49,
    "preview": "from .scripted_condition import ScriptedCondition"
  },
  {
    "path": "Automation/conditions/scripted_condition/metadata.json",
    "chars": 143,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"ScriptedCondition\"],\n  \"tentac"
  },
  {
    "path": "Automation/conditions/scripted_condition/scripted_condition.py",
    "chars": 4533,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/trigger_events/period_check_event/__init__.py",
    "chars": 39,
    "preview": "from .period_check import PeriodicCheck"
  },
  {
    "path": "Automation/trigger_events/period_check_event/metadata.json",
    "chars": 139,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"PeriodicCheck\"],\n  \"tentacles-"
  },
  {
    "path": "Automation/trigger_events/period_check_event/period_check.py",
    "chars": 2200,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/trigger_events/price_threshold_event/__init__.py",
    "chars": 43,
    "preview": "from .price_threshold import PriceThreshold"
  },
  {
    "path": "Automation/trigger_events/price_threshold_event/metadata.json",
    "chars": 140,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"PriceThreshold\"],\n  \"tentacles"
  },
  {
    "path": "Automation/trigger_events/price_threshold_event/price_threshold.py",
    "chars": 5549,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Automation/trigger_events/profitability_threshold_event/__init__.py",
    "chars": 59,
    "preview": "from .profitability_threshold import ProfitabilityThreshold"
  },
  {
    "path": "Automation/trigger_events/profitability_threshold_event/metadata.json",
    "chars": 148,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"ProfitabilityThreshold\"],\n  \"t"
  },
  {
    "path": "Automation/trigger_events/profitability_threshold_event/profitability_threshold.py",
    "chars": 6876,
    "preview": "#  This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)\n#  Copyright (c) 2023 Drakkar-Software, Al"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/__init__.py",
    "chars": 88,
    "preview": "from .bot_snapshot_with_history_collector import ExchangeBotSnapshotWithHistoryCollector"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/bot_snapshot_with_history_collector.py",
    "chars": 22969,
    "preview": "#  Drakkar-Software OctoBot\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free software; "
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_bot_snapshot_data_collector/metadata.json",
    "chars": 154,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"ExchangeBotSnapshotCollector\"]"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_history_collector/__init__.py",
    "chars": 59,
    "preview": "from .history_collector import ExchangeHistoryDataCollector"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_history_collector/history_collector.pxd",
    "chars": 995,
    "preview": "# cython: language_level=3\n#  Drakkar-Software OctoBot-Backtesting\n#  Copyright (c) Drakkar-Software, All rights reserve"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_history_collector/history_collector.py",
    "chars": 9924,
    "preview": "#  Drakkar-Software OctoBot\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free software; "
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_history_collector/metadata.json",
    "chars": 154,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"ExchangeHistoryDataCollector\"]"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_history_collector/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_history_collector/tests/test_history_collector.py",
    "chars": 12173,
    "preview": "#  Drakkar-Software OctoBot\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free software; "
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_live_collector/__init__.py",
    "chars": 53,
    "preview": "from .live_collector import ExchangeLiveDataCollector"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_live_collector/live_collector.pxd",
    "chars": 907,
    "preview": "# cython: language_level=3\n#  Drakkar-Software OctoBot-Backtesting\n#  Copyright (c) Drakkar-Software, All rights reserve"
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_live_collector/live_collector.py",
    "chars": 5391,
    "preview": "#  Drakkar-Software OctoBot\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free software; "
  },
  {
    "path": "Backtesting/collectors/exchanges/exchange_live_collector/metadata.json",
    "chars": 151,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"ExchangeLiveDataCollector\"],\n "
  },
  {
    "path": "Backtesting/converters/exchanges/legacy_data_converter/__init__.py",
    "chars": 49,
    "preview": "from .legacy_converter import LegacyDataConverter"
  },
  {
    "path": "Backtesting/converters/exchanges/legacy_data_converter/legacy_converter.pxd",
    "chars": 1200,
    "preview": "# cython: language_level=3\n#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved."
  },
  {
    "path": "Backtesting/converters/exchanges/legacy_data_converter/legacy_converter.py",
    "chars": 7263,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Backtesting/converters/exchanges/legacy_data_converter/metadata.json",
    "chars": 145,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"LegacyDataConverter\"],\n  \"tent"
  },
  {
    "path": "Backtesting/importers/exchanges/generic_exchange_importer/__init__.py",
    "chars": 66,
    "preview": "from .generic_exchange_importer import GenericExchangeDataImporter"
  },
  {
    "path": "Backtesting/importers/exchanges/generic_exchange_importer/generic_exchange_importer.pxd",
    "chars": 903,
    "preview": "# cython: language_level=3\n#  Drakkar-Software OctoBot-Backtesting\n#  Copyright (c) Drakkar-Software, All rights reserve"
  },
  {
    "path": "Backtesting/importers/exchanges/generic_exchange_importer/generic_exchange_importer.py",
    "chars": 848,
    "preview": "#  Drakkar-Software OctoBot-Backtesting\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is fre"
  },
  {
    "path": "Backtesting/importers/exchanges/generic_exchange_importer/metadata.json",
    "chars": 153,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"GenericExchangeDataImporter\"],"
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/__init__.py",
    "chars": 82,
    "preview": "from .instant_fluctuations import InstantFluctuationsEvaluator, InstantMAEvaluator"
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/config/InstantFluctuationsEvaluator.json",
    "chars": 119,
    "preview": "{\n    \"price_difference_threshold_percent\": 1,\n    \"volume_difference_threshold_percent\": 400,\n    \"time_frame\": \"1m\"\n}"
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/config/InstantMAEvaluator.json",
    "chars": 65,
    "preview": "{\n    \"period\": 6,\n    \"time_frame\": \"1m\",\n    \"threshold\": 0.5\n}"
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/instant_fluctuations.py",
    "chars": 14969,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/metadata.json",
    "chars": 176,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"InstantFluctuationsEvaluator\","
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/resources/InstantFluctuationsEvaluator.md",
    "chars": 193,
    "preview": "Triggers when a superior to 1% change of price or a superior to x4 change of volume from recent average happens.\n\nThe pr"
  },
  {
    "path": "Evaluator/RealTime/instant_fluctuations_evaluator/resources/InstantMAEvaluator.md",
    "chars": 315,
    "preview": "Uses a [moving average](https://www.investopedia.com/terms/m/movingaverage.asp) \ncomputed on close prices to set its eva"
  },
  {
    "path": "Evaluator/Social/forum_evaluator/__init__.py",
    "chars": 39,
    "preview": "from .forum import RedditForumEvaluator"
  },
  {
    "path": "Evaluator/Social/forum_evaluator/config/RedditForumEvaluator.json",
    "chars": 1458,
    "preview": "{\n    \"crypto-currencies\": [\n        {\n            \"crypto-currency\": \"Bitcoin\",\n            \"subreddits\": [\n           "
  },
  {
    "path": "Evaluator/Social/forum_evaluator/forum.py",
    "chars": 6817,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Social/forum_evaluator/metadata.json",
    "chars": 210,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"RedditForumEvaluator\"],\n  \"ten"
  },
  {
    "path": "Evaluator/Social/forum_evaluator/resources/RedditForumEvaluator.md",
    "chars": 218,
    "preview": "First initialises using the recent history of the subreddits in RedditForumEvaluator.json then\nwatches for new posts to "
  },
  {
    "path": "Evaluator/Social/news_evaluator/__init__.py",
    "chars": 38,
    "preview": "from .news import TwitterNewsEvaluator"
  },
  {
    "path": "Evaluator/Social/news_evaluator/config/TwitterNewsEvaluator.json",
    "chars": 2117,
    "preview": "{\n    \"crypto-currencies\": [\n        {\n            \"crypto-currency\": \"Bitcoin\",\n            \"accounts\": [\n             "
  },
  {
    "path": "Evaluator/Social/news_evaluator/metadata.json",
    "chars": 185,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"TwitterNewsEvaluator\"],\n  \"ten"
  },
  {
    "path": "Evaluator/Social/news_evaluator/news.py",
    "chars": 9409,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Social/news_evaluator/resources/TwitterNewsEvaluator.md",
    "chars": 225,
    "preview": "Triggers when a new tweet appears from a Twitter account in TwitterNewsEvaluator.json.\n\nIf the evaluation of any given t"
  },
  {
    "path": "Evaluator/Social/signal_evaluator/__init__.py",
    "chars": 75,
    "preview": "from .signal import TelegramSignalEvaluator, TelegramChannelSignalEvaluator"
  },
  {
    "path": "Evaluator/Social/signal_evaluator/config/TelegramChannelSignalEvaluator.json",
    "chars": 281,
    "preview": "{\n    \"telegram-channels\": [\n        {\n            \"channel_name\": \"Test-Channel\",\n            \"signal_pair\": \"Pair: (.*"
  },
  {
    "path": "Evaluator/Social/signal_evaluator/config/TelegramSignalEvaluator.json",
    "chars": 73,
    "preview": "{\n    \"telegram-channels\": [\n        \"test_telegram_signal_strat\"\n    ]\n}"
  },
  {
    "path": "Evaluator/Social/signal_evaluator/metadata.json",
    "chars": 206,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"TelegramSignalEvaluator\", \"Tel"
  },
  {
    "path": "Evaluator/Social/signal_evaluator/resources/TelegramChannelSignalEvaluator.md",
    "chars": 459,
    "preview": "Evaluator that catch Telegram channel signals.\n\nTriggers on a Telegram signal from any channel your personal account joi"
  },
  {
    "path": "Evaluator/Social/signal_evaluator/resources/TelegramSignalEvaluator.md",
    "chars": 917,
    "preview": "Very simple evaluator designed to be an example for an evaluator using Telegram signals.\n\nTriggers on a Telegram signal "
  },
  {
    "path": "Evaluator/Social/signal_evaluator/signal.py",
    "chars": 10669,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Social/signal_evaluator/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Social/signal_evaluator/tests/test_telegram_channel_signal_evaluator.py",
    "chars": 7201,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Social/trends_evaluator/__init__.py",
    "chars": 41,
    "preview": "from .trends import GoogleTrendsEvaluator"
  },
  {
    "path": "Evaluator/Social/trends_evaluator/config/GoogleTrendsEvaluator.json",
    "chars": 70,
    "preview": "{\n  \"refresh_rate_seconds\" : 86400,\n  \"relevant_history_months\" : 3\n}\n"
  },
  {
    "path": "Evaluator/Social/trends_evaluator/metadata.json",
    "chars": 191,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"GoogleTrendsEvaluator\"],\n  \"te"
  },
  {
    "path": "Evaluator/Social/trends_evaluator/resources/GoogleTrendsEvaluator.md",
    "chars": 265,
    "preview": "Analyses the popularity of the given currencies using their names. \n\nData are provided by [Google's trends service](http"
  },
  {
    "path": "Evaluator/Social/trends_evaluator/trends.py",
    "chars": 4170,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/blank_strategy_evaluator/__init__.py",
    "chars": 51,
    "preview": "from .blank_strategy import BlankStrategyEvaluator\n"
  },
  {
    "path": "Evaluator/Strategies/blank_strategy_evaluator/blank_strategy.py",
    "chars": 2343,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/blank_strategy_evaluator/config/BlankStrategyEvaluator.json",
    "chars": 148,
    "preview": "{\n  \"required_time_frames\" : [\"1h\"],\n  \"required_evaluators\" : [\"*\"],\n  \"required_candles_count\" : 200,\n  \"default_confi"
  },
  {
    "path": "Evaluator/Strategies/blank_strategy_evaluator/metadata.json",
    "chars": 148,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"BlankStrategyEvaluator\"],\n  \"t"
  },
  {
    "path": "Evaluator/Strategies/blank_strategy_evaluator/resources/BlankStrategyEvaluator.md",
    "chars": 75,
    "preview": "BlankStrategyEvaluator is forwarding evaluator values to the trading mode.\n"
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/__init__.py",
    "chars": 63,
    "preview": "from .dip_analyser_strategy import DipAnalyserStrategyEvaluator"
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/config/DipAnalyserStrategyEvaluator.json",
    "chars": 365,
    "preview": "{\n    \"default_config\": [\n        \"KlingerOscillatorReversalConfirmationMomentumEvaluator\",\n        \"RSIWeightMomentumEv"
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/dip_analyser_strategy.py",
    "chars": 5409,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/metadata.json",
    "chars": 177,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"DipAnalyserStrategyEvaluator\"]"
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/resources/DipAnalyserStrategyEvaluator.md",
    "chars": 1044,
    "preview": "DipAnalyserStrategyEvaluator is a strategy analysing market dips using [RSI](https://www.investopedia.com/terms/r/rsi.as"
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/dip_analyser_strategy_evaluator/tests/test_dip_analyser_strategy_evaluator.py",
    "chars": 4089,
    "preview": "#  Drakkar-Software OctoBot\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free software; "
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/__init__.py",
    "chars": 89,
    "preview": "from .mixed_strategies import SimpleStrategyEvaluator, TechnicalAnalysisStrategyEvaluator"
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/config/SimpleStrategyEvaluator.json",
    "chars": 462,
    "preview": "{\n    \"default_config\": [\n        \"DoubleMovingAverageTrendEvaluator\",\n        \"RSIMomentumEvaluator\"\n    ],\n    \"requir"
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/config/TechnicalAnalysisStrategyEvaluator.json",
    "chars": 737,
    "preview": "{\n    \"compatible_evaluator_types\": [\n        \"TA\",\n        \"REAL_TIME\"\n    ],\n    \"default_config\": [\n        \"DoubleMo"
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/metadata.json",
    "chars": 187,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"SimpleStrategyEvaluator\", \"Tec"
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/mixed_strategies.py",
    "chars": 22251,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/resources/SimpleStrategyEvaluator.md",
    "chars": 454,
    "preview": "SimpleStrategyEvaluator is the most flexible strategy. Meant to be customized, it is using\nevery activated technical, so"
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/resources/TechnicalAnalysisStrategyEvaluator.md",
    "chars": 1042,
    "preview": "TechnicalAnalysisStrategyEvaluator a flexible technical analysis strategy. Meant to be customized, it is using \nevery ac"
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/tests/test_simple_strategy_evaluator.py",
    "chars": 3987,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/mixed_strategies_evaluator/tests/test_technical_analysis_strategy_evaluator.py",
    "chars": 4027,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/__init__.py",
    "chars": 63,
    "preview": "from .move_signals_strategy import MoveSignalsStrategyEvaluator"
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/config/MoveSignalsStrategyEvaluator.json",
    "chars": 255,
    "preview": "{\n  \"required_time_frames\" : [\"30m\", \"1h\", \"4h\"],\n  \"required_evaluators\" : [\"InstantFluctuationsEvaluator\", \"KlingerOsc"
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/metadata.json",
    "chars": 177,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"MoveSignalsStrategyEvaluator\"]"
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/move_signals_strategy.py",
    "chars": 8527,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/resources/MoveSignalsStrategyEvaluator.md",
    "chars": 1011,
    "preview": "MoveSignalsStrategyEvaluator is a fractal strategy: it is using different time frames to\nbalance decisions. \n\nThis strat"
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Strategies/move_signals_strategy_evaluator/tests/test_move_signals_strategy_evaluator.py",
    "chars": 4012,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/ai_evaluator/__init__.py",
    "chars": 28,
    "preview": "from .ai import GPTEvaluator"
  },
  {
    "path": "Evaluator/TA/ai_evaluator/ai.py",
    "chars": 17423,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/ai_evaluator/config/GPTEvaluator.json",
    "chars": 195,
    "preview": "{\n    \"indicator\": \"No indicator: raw candles price data\",\n    \"period\": 2,\n    \"source\": \"Close\",\n    \"min_confidence_t"
  },
  {
    "path": "Evaluator/TA/ai_evaluator/metadata.json",
    "chars": 138,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"GPTEvaluator\"],\n  \"tentacles-r"
  },
  {
    "path": "Evaluator/TA/ai_evaluator/resources/GPTEvaluator.md",
    "chars": 1504,
    "preview": "Uses [Chat GPT](https://chat.openai.com/) to predict the next moves of the market.\n\nEvaluates between -1 to 1 according "
  },
  {
    "path": "Evaluator/TA/ai_evaluator/tests/test_ai.py",
    "chars": 2826,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/__init__.py",
    "chars": 262,
    "preview": "from .momentum import RSIMomentumEvaluator, ADXMomentumEvaluator, RSIWeightMomentumEvaluator, \\\n    BBMomentumEvaluator,"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/ADXMomentumEvaluator.json",
    "chars": 27,
    "preview": "{\n    \"period_length\": 14\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/BBMomentumEvaluator.json",
    "chars": 27,
    "preview": "{\n    \"period_length\": 20\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/EMAMomentumEvaluator.json",
    "chars": 61,
    "preview": "{\n    \"period_length\": 21,\n    \"price_threshold_percent\": 2\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/KlingerOscillatorMomentumEvaluator.json",
    "chars": 78,
    "preview": "{\n    \"ema_signal_period\": 13,\n    \"long_period\": 55,\n    \"short_period\": 35\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/KlingerOscillatorReversalConfirmationMomentumEvaluator.json",
    "chars": 78,
    "preview": "{\n    \"ema_signal_period\": 13,\n    \"long_period\": 55,\n    \"short_period\": 35\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/MACDMomentumEvaluator.json",
    "chars": 94,
    "preview": "{\n    \"long_period_length\": 26,\n    \"short_period_length\": 12,\n    \"signal_period_length\": 9\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/RSIMomentumEvaluator.json",
    "chars": 117,
    "preview": "{\n    \"long_threshold\": 30,\n    \"period_length\": 14,\n    \"short_threshold\": 70,\n    \"trend_change_identifier\": true\n}"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/config/RSIWeightMomentumEvaluator.json",
    "chars": 2487,
    "preview": "{\n  \"period\": 14,\n  \"slow_eval_count\": 16,\n  \"fast_eval_count\": 4,\n  \"RSI_to_weight\": [\n    {\n      \"slow_threshold\": 30"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/metadata.json",
    "chars": 376,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"RSIMomentumEvaluator\", \"ADXMom"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/momentum.py",
    "chars": 44571,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/ADXMomentumEvaluator.md",
    "chars": 490,
    "preview": "Uses the [Average Directional Index](https://www.investopedia.com/terms/a/adx.asp)  \nto find reversals. The default impl"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/BBMomentumEvaluator.md",
    "chars": 254,
    "preview": "Uses the [Bollinger bands](https://www.investopedia.com/terms/b/bollingerbands.asp)  to evaluate a value from -1 to 1 ac"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/EMAMomentumEvaluator.md",
    "chars": 226,
    "preview": "Uses  [exponential moving averages](https://www.investopedia.com/terms/m/movingaverage.asp) to find signal when the curr"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/KlingerOscillatorMomentumEvaluator.md",
    "chars": 219,
    "preview": "Uses [Klinger Oscillator](https://www.investopedia.com/terms/k/klingeroscillator.asp) to find reversals.\n\nEvaluates -1 t"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/KlingerOscillatorReversalConfirmationMomentumEvaluator.md",
    "chars": 144,
    "preview": "Uses [Klinger Oscillator](https://www.investopedia.com/terms/k/klingeroscillator.asp) to find reversals.\n\nReturns True o"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/MACDMomentumEvaluator.md",
    "chars": 304,
    "preview": "Uses the [Moving Average Convergence Divergence](https://www.investopedia.com/terms/m/macd.asp) to find reversals.\n\nThis"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/RSIMomentumEvaluator.md",
    "chars": 223,
    "preview": "Uses the [Relative Strength Index](https://www.investopedia.com/terms/r/rsi.asp) to find trend reversals. \n\nWhen found, "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/resources/RSIWeightMomentumEvaluator.md",
    "chars": 138,
    "preview": "Uses the [Relative Strength Index](https://www.investopedia.com/terms/r/rsi.asp) to find dips and give them weight accor"
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/tests/test_adx_momentum_evaluator.py",
    "chars": 2987,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/tests/test_bollinger_bands_momentum_TA_evaluator.py",
    "chars": 2983,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/tests/test_klinger_TA_evaluator.py",
    "chars": 3077,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/tests/test_macd_TA_evaluator.py",
    "chars": 3009,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/momentum_evaluator/tests/test_rsi_TA_evaluator.py",
    "chars": 2959,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/trend_evaluator/__init__.py",
    "chars": 132,
    "preview": "from .trend import DoubleMovingAverageTrendEvaluator, EMADivergenceTrendEvaluator, DeathAndGoldenCrossEvaluator, SuperTr"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/config/DeathAndGoldenCrossEvaluator.json",
    "chars": 103,
    "preview": "{\n    \"fast_length\": 50,\n    \"slow_length\": 200,\n    \"slow_ma_type\": \"SMA\",\n    \"fast_ma_type\": \"SMA\"\n}"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/config/DoubleMovingAverageTrendEvaluator.json",
    "chars": 62,
    "preview": "{\n    \"long_period_length\": 10,\n    \"short_period_length\": 5\n}"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/config/EMADivergenceTrendEvaluator.json",
    "chars": 50,
    "preview": "{\n    \"size\": 50,\n    \"short\": -2,\n    \"long\": 2\n}"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/config/SuperTrendEvaluator.json",
    "chars": 31,
    "preview": "{\n\t\"factor\": 3,\n\t\"length\": 10\n}"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/metadata.json",
    "chars": 245,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"DoubleMovingAverageTrendEvalua"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/resources/DeathAndGoldenCrossEvaluator.md",
    "chars": 764,
    "preview": "DeathAndGoldenCrossEvaluator is based on two [moving averages](https://www.investopedia.com/terms/m/movingaverage.asp), "
  },
  {
    "path": "Evaluator/TA/trend_evaluator/resources/DoubleMovingAverageTrendEvaluator.md",
    "chars": 309,
    "preview": "Uses two [moving averages](https://www.investopedia.com/terms/m/movingaverage.asp) (a slow and a fast one) to find rever"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/resources/EMADivergenceTrendEvaluator.md",
    "chars": 179,
    "preview": "Uses [exponential moving averages](https://www.investopedia.com/terms/e/ema.asp) to find price divergences.\n\nEvaluates f"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/resources/SuperTrendEvaluator.md",
    "chars": 359,
    "preview": "SuperTrendEvaluator is a trend-following indicator based on Average True Range [ATR](https://www.tradingview.com/scripts"
  },
  {
    "path": "Evaluator/TA/trend_evaluator/tests/__init__.py",
    "chars": 718,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/trend_evaluator/tests/test_double_moving_averages_TA_evaluator.py",
    "chars": 3037,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/trend_evaluator/trend.py",
    "chars": 18856,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/TA/volatility_evaluator/__init__.py",
    "chars": 56,
    "preview": "from .volatility import StochasticRSIVolatilityEvaluator"
  },
  {
    "path": "Evaluator/TA/volatility_evaluator/config/StochasticRSIVolatilityEvaluator.json",
    "chars": 62,
    "preview": "{\n    \"period\": 14,\n    \"low_level\": 1,\n    \"high_level\": 98\n}"
  },
  {
    "path": "Evaluator/TA/volatility_evaluator/metadata.json",
    "chars": 158,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"StochasticRSIVolatilityEvaluat"
  },
  {
    "path": "Evaluator/TA/volatility_evaluator/resources/StochasticRSIVolatilityEvaluator.md",
    "chars": 197,
    "preview": "Uses the [Stochastic RSI](https://www.investopedia.com/terms/s/stochrsi.asp) as a volatilty evaluator to identify trends"
  },
  {
    "path": "Evaluator/TA/volatility_evaluator/volatility.py",
    "chars": 4258,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/candles_util/__init__.py",
    "chars": 37,
    "preview": "from .candles_util import CandlesUtil"
  },
  {
    "path": "Evaluator/Util/candles_util/candles_util.pxd",
    "chars": 1032,
    "preview": "# cython: language_level=3\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#"
  },
  {
    "path": "Evaluator/Util/candles_util/candles_util.py",
    "chars": 3576,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/candles_util/metadata.json",
    "chars": 137,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"CandlesUtil\"],\n  \"tentacles-re"
  },
  {
    "path": "Evaluator/Util/candles_util/tests/test_candles_util.py",
    "chars": 7151,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/overall_state_analysis/__init__.py",
    "chars": 56,
    "preview": "from .overall_state_analysis import OverallStateAnalyser"
  },
  {
    "path": "Evaluator/Util/overall_state_analysis/metadata.json",
    "chars": 146,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"OverallStateAnalyser\"],\n  \"ten"
  },
  {
    "path": "Evaluator/Util/overall_state_analysis/overall_state_analysis.py",
    "chars": 1923,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/pattern_analysis/__init__.py",
    "chars": 45,
    "preview": "from .pattern_analysis import PatternAnalyser"
  },
  {
    "path": "Evaluator/Util/pattern_analysis/metadata.json",
    "chars": 141,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"PatternAnalyser\"],\n  \"tentacle"
  },
  {
    "path": "Evaluator/Util/pattern_analysis/pattern_analysis.py",
    "chars": 3903,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/statistics_analysis/__init__.py",
    "chars": 50,
    "preview": "from .statistics_analysis import StatisticAnalysis"
  },
  {
    "path": "Evaluator/Util/statistics_analysis/metadata.json",
    "chars": 143,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"StatisticAnalysis\"],\n  \"tentac"
  },
  {
    "path": "Evaluator/Util/statistics_analysis/statistics_analysis.py",
    "chars": 2788,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/text_analysis/__init__.py",
    "chars": 39,
    "preview": "from .text_analysis import TextAnalysis"
  },
  {
    "path": "Evaluator/Util/text_analysis/metadata.json",
    "chars": 138,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"TextAnalysis\"],\n  \"tentacles-r"
  },
  {
    "path": "Evaluator/Util/text_analysis/text_analysis.py",
    "chars": 6734,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "Evaluator/Util/trend_analysis/__init__.py",
    "chars": 41,
    "preview": "from .trend_analysis import TrendAnalysis"
  },
  {
    "path": "Evaluator/Util/trend_analysis/metadata.json",
    "chars": 139,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [\"TrendAnalysis\"],\n  \"tentacles-"
  },
  {
    "path": "Evaluator/Util/trend_analysis/trend_analysis.py",
    "chars": 6000,
    "preview": "#  Drakkar-Software OctoBot-Tentacles\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free "
  },
  {
    "path": "LICENSE",
    "chars": 7652,
    "preview": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007"
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/__init__.py",
    "chars": 1391,
    "preview": "# pylint: disable=R0801\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n# "
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/exchange_operator.py",
    "chars": 1566,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/exchange_private_data_operators/__init__.py",
    "chars": 1091,
    "preview": "# pylint: disable=R0801\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n# "
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/exchange_private_data_operators/portfolio_operators.py",
    "chars": 3519,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/exchange_public_data_operators/__init__.py",
    "chars": 1124,
    "preview": "# pylint: disable=R0801\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n# "
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/exchange_public_data_operators/ohlcv_operators.py",
    "chars": 11873,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/metadata.json",
    "chars": 124,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"origin_package\": \"OctoBot-Default-Tentacles\",\n  \"tentacles\": [],\n  \"tentacles-requirements\": "
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/tests/__init__.py",
    "chars": 12120,
    "preview": "#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free so"
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/tests/exchange_public_data_operators/test_ohlcv_operators.py",
    "chars": 12120,
    "preview": "#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free so"
  },
  {
    "path": "Meta/DSL_operators/exchange_operators/tests/test_mocks.py",
    "chars": 8940,
    "preview": "#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n#  This library is free so"
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/__init__.py",
    "chars": 3998,
    "preview": "# pylint: disable=R0801\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All rights reserved.\n#\n# "
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_binary_operators.py",
    "chars": 4187,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_call_operators.py",
    "chars": 6382,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_compare_operators.py",
    "chars": 5851,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_expression_operators.py",
    "chars": 2637,
    "preview": "# pylint: disable=missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All "
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_iterable_operators.py",
    "chars": 1499,
    "preview": "# pylint: disable=missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All "
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_name_operators.py",
    "chars": 1731,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_nary_operators.py",
    "chars": 1968,
    "preview": "# pylint: disable=missing-class-docstring,missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c"
  },
  {
    "path": "Meta/DSL_operators/python_std_operators/base_subscripting_operators.py",
    "chars": 5658,
    "preview": "# pylint: disable=missing-function-docstring\n#  Drakkar-Software OctoBot-Commons\n#  Copyright (c) Drakkar-Software, All "
  }
]

// ... and 832 more files (download for full content)

About this extraction

This page contains the full source code of the Drakkar-Software/OctoBot-Tentacles GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1032 files (4.8 MB), approximately 1.3M tokens, and a symbol index with 3968 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!