├── .dockerignore
├── .github
├── CODEOWNERS
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── SECURITY.md
├── dependabot.yml
└── workflows
│ ├── docker.yml
│ ├── main.yml
│ └── release.yml
├── .gitignore
├── .gitpod.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DELIVERY.md
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── README.md
├── additional_tests
├── exchanges_tests
│ ├── .env.template
│ ├── __init__.py
│ ├── abstract_authenticated_exchange_tester.py
│ ├── abstract_authenticated_future_exchange_tester.py
│ ├── test_ascendex.py
│ ├── test_binance.py
│ ├── test_binance_futures.py
│ ├── test_bingx.py
│ ├── test_bitget.py
│ ├── test_bitmart.py
│ ├── test_bybit.py
│ ├── test_bybit_futures.py
│ ├── test_coinbase.py
│ ├── test_coinbase_ed25519.py
│ ├── test_coinex.py
│ ├── test_cryptocom.py
│ ├── test_gateio.py
│ ├── test_hollaex.py
│ ├── test_htx.py
│ ├── test_hyperliquid.py
│ ├── test_kucoin.py
│ ├── test_kucoin_futures.py
│ ├── test_mexc.py
│ ├── test_okx.py
│ ├── test_okx_futures.py
│ └── test_phemex.py
├── historical_backend_tests
│ ├── .env.template
│ ├── __init__.py
│ ├── test_clickhouse_candles.py
│ └── test_iceberg_candles.py
└── supabase_backend_tests
│ ├── .env.template
│ ├── __init__.py
│ ├── _test_realtime.py
│ ├── test_bot_elements.py
│ ├── test_cryptocurrencies.py
│ ├── test_storage.py
│ └── test_user_elements.py
├── dev_requirements.txt
├── docker-compose.https.yml
├── docker-compose.yml
├── docker
├── README.md
├── docker-entrypoint.sh
└── tunnel.sh
├── octobot.hcl
├── octobot
├── README.md
├── __init__.py
├── api
│ ├── __init__.py
│ ├── backtesting.py
│ ├── strategy_optimizer.py
│ └── updater.py
├── automation
│ ├── __init__.py
│ ├── automation.py
│ └── bases
│ │ ├── __init__.py
│ │ ├── abstract_action.py
│ │ ├── abstract_condition.py
│ │ ├── abstract_trigger_event.py
│ │ └── automation_step.py
├── backtesting
│ ├── __init__.py
│ ├── abstract_backtesting_test.py
│ ├── independent_backtesting.py
│ ├── minimal_data_importer.py
│ └── octobot_backtesting.py
├── channels
│ ├── __init__.py
│ └── octobot_channel.py
├── cli.py
├── commands.py
├── community
│ ├── __init__.py
│ ├── authentication.py
│ ├── community_analysis.py
│ ├── community_manager.py
│ ├── errors.py
│ ├── errors_upload
│ │ ├── __init__.py
│ │ ├── sentry_aiohttp_transport.py
│ │ └── sentry_tracker.py
│ ├── feeds
│ │ ├── __init__.py
│ │ ├── abstract_feed.py
│ │ ├── community_mqtt_feed.py
│ │ ├── community_supabase_feed.py
│ │ ├── community_ws_feed.py
│ │ └── feed_factory.py
│ ├── graphql_requests.py
│ ├── history_backend
│ │ ├── __init__.py
│ │ ├── clickhouse_historical_backend_client.py
│ │ ├── historical_backend_client.py
│ │ ├── history_backend_factory.py
│ │ ├── iceberg_historical_backend_client.py
│ │ └── util.py
│ ├── identifiers_provider.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── community_donation.py
│ │ ├── community_fields.py
│ │ ├── community_public_data.py
│ │ ├── community_supports.py
│ │ ├── community_tentacles_package.py
│ │ ├── community_user_account.py
│ │ ├── executed_product_details.py
│ │ ├── formatters.py
│ │ ├── startup_info.py
│ │ └── strategy_data.py
│ ├── supabase_backend
│ │ ├── __init__.py
│ │ ├── community_supabase_client.py
│ │ ├── configuration_storage.py
│ │ ├── enums.py
│ │ ├── error_translator.py
│ │ └── supabase_client.py
│ └── tentacles_packages.py
├── config
│ ├── config_schema.json
│ ├── default_config.json
│ ├── logging_config.ini
│ └── profile_schema.json
├── configuration_manager.py
├── constants.py
├── databases_util.py
├── disclaimer.py
├── enums.py
├── errors.py
├── initializer.py
├── limits.py
├── logger.py
├── octobot.py
├── octobot_api.py
├── octobot_backtesting_factory.py
├── octobot_channel_consumer.py
├── producers
│ ├── __init__.py
│ ├── evaluator_producer.py
│ ├── exchange_producer.py
│ ├── interface_producer.py
│ └── service_feed_producer.py
├── storage
│ ├── __init__.py
│ ├── db_databases_pruning.py
│ └── trading_metadata.py
├── strategy_optimizer
│ ├── __init__.py
│ ├── fitness_parameter.py
│ ├── optimizer_constraint.py
│ ├── optimizer_data_files
│ │ ├── AbstractExchangeHistoryCollector_1581774950.9324272.data
│ │ ├── AbstractExchangeHistoryCollector_1581774962.1269426.data
│ │ ├── AbstractExchangeHistoryCollector_1581774974.669779.data
│ │ ├── AbstractExchangeHistoryCollector_1581774982.726014.data
│ │ ├── AbstractExchangeHistoryCollector_1581774988.7215023.data
│ │ ├── AbstractExchangeHistoryCollector_1581774995.2311237.data
│ │ ├── AbstractExchangeHistoryCollector_1581775018.2658834.data
│ │ ├── AbstractExchangeHistoryCollector_1581775026.9255266.data
│ │ ├── AbstractExchangeHistoryCollector_1581775117.1713624.data
│ │ ├── AbstractExchangeHistoryCollector_1581775133.1533682.data
│ │ ├── AbstractExchangeHistoryCollector_1581775139.0332782.data
│ │ ├── AbstractExchangeHistoryCollector_1581775144.480404.data
│ │ ├── AbstractExchangeHistoryCollector_1581775149.6372743.data
│ │ ├── AbstractExchangeHistoryCollector_1581775154.2598503.data
│ │ ├── AbstractExchangeHistoryCollector_1581776404.9679003.data
│ │ ├── AbstractExchangeHistoryCollector_1581776676.5721796.data
│ │ └── ExchangeHistoryDataCollector_1711122002.311132.data
│ ├── optimizer_filter.py
│ ├── optimizer_settings.py
│ ├── scored_run_result.py
│ ├── strategy_design_optimizer.py
│ ├── strategy_design_optimizer_factory.py
│ ├── strategy_optimizer.py
│ ├── strategy_test_suite.py
│ └── test_suite_result.py
├── task_manager.py
└── updater
│ ├── __init__.py
│ ├── binary_updater.py
│ ├── python_updater.py
│ ├── updater.py
│ └── updater_factory.py
├── requirements.txt
├── setup.cfg
├── setup.py
├── standard.rc
├── start.py
└── tests
├── Dockerfile
├── __init__.py
├── docker-entrypoint.sh
├── functional_tests
├── __init__.py
├── automations
│ ├── __init__.py
│ └── test_automation.py
├── backtesting_tests
│ ├── __init__.py
│ └── backtesting_test.py
├── evaluators_tests
│ ├── __init__.py
│ └── abstract_TA_test.py
├── launch_test
│ ├── __init__.py
│ └── launch_test.py
└── strategy_evaluators_tests
│ ├── __init__.py
│ └── abstract_strategy_test.py
├── static
├── AbstractExchangeHistoryCollector_1586017993.616272.data
├── ExchangeHistoryDataCollector_1587769859.9278197.data
├── ExchangeHistoryDataCollector_1588110698.1060486.data
├── config.json
├── default_tentacles_config.json
├── profile.json
├── specific_config
│ ├── ArbitrageTradingMode.json
│ ├── DailyTradingMode.json
│ ├── DipAnalyserStrategyEvaluator.json
│ ├── DipAnalyserTradingMode.json
│ ├── EMADivergenceTrendEvaluator.json
│ ├── GoogleTrendsEvaluator.json
│ ├── InstantFluctuationsEvaluator.json
│ ├── MoveSignalsStrategyEvaluator.json
│ ├── RSIWeightMomentumEvaluator.json
│ ├── RedditForumEvaluator.json
│ ├── SignalTradingMode.json
│ ├── SimpleStrategyEvaluator.json
│ ├── StaggeredOrdersTradingMode.json
│ ├── StochasticRSIVolatilityEvaluator.json
│ ├── TechnicalAnalysisStrategyEvaluator.json
│ ├── TelegramSignalEvaluator.json
│ ├── TradingViewSignalsTradingMode.json
│ ├── TwitterNewsEvaluator.json
│ └── hollaex.json
├── tentacles_config.json
└── trading_states_tests
│ ├── ko_missing_trader_data.json
│ ├── ko_missing_trader_s_data.json
│ ├── ko_ref_invalid_json.json
│ ├── ko_ref_missing_exchange.json
│ ├── ko_ref_missing_key.json
│ ├── ko_ref_ref_market_changed.json
│ ├── ko_ref_symbols_changed.json
│ ├── ok2_log.12
│ ├── ok_log.1
│ └── ok_ref.json
├── test_utils
├── __init__.py
├── bot_management.py
├── config.py
├── data_bank.py
├── logging.py
├── memory_check_util.py
├── order_util.py
├── test_exchanges.py
└── trading_modes.py
└── unit_tests
├── __init__.py
├── community
├── __init__.py
├── errors_upload
│ ├── __init__.py
│ └── test_sentry_aiohttp_transport.py
├── test_authentication.py
├── test_community_data.py
├── test_community_mqtt_feed.py
├── test_community_supabase_client.py
├── test_community_supabase_feed.py
└── test_community_ws_feed.py
├── strategy_optimizer
├── __init__.py
├── test_strategy_design_optimizer.py
├── test_strategy_optimizer.py
└── test_strategy_test_suite.py
├── test_configuration_manager.py
├── trading_modes_tests
├── __init__.py
└── trading_mode_test_toolkit.py
└── updater
├── __init__.py
└── test_updater.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | # dev
2 | .idea
3 |
4 | # CI files
5 | .coveragerc
6 | .coveralls.yml
7 | .travis.yml
8 | appveyor.yml
9 | renovate.json
10 | setup.cfg
11 | tox.ini
12 |
13 | # octobot
14 | tentacles
15 | user
16 | logs
17 |
18 | # Git
19 | .git
20 | Dockerfile
21 | .DS_Store
22 | .gitignore
23 | .dockerignore
24 | .github
25 |
26 | # Files that might appear in the root of a volume
27 | .DocumentRevisions-V100
28 | .fseventsd
29 | .Spotlight-V100
30 | .TemporaryItems
31 | .Trashes
32 | .VolumeIcon.icns
33 | .com.apple.timemachine.donotpresent
34 |
35 | # Directories potentially created on remote AFP share
36 | .AppleDB
37 | .AppleDesktop
38 | Network Trash Folder
39 | Temporary Items
40 | .apdisk
41 |
42 | # Byte-compiled / optimized / DLL files
43 | __pycache__/
44 | *.py[cod]
45 | *$py.class
46 |
47 | # C extensions
48 | *.so
49 |
50 | # Distribution / packaging
51 | .Python
52 | build/
53 | develop-eggs/
54 | dist/
55 | downloads/
56 | eggs/
57 | .eggs/
58 | lib64/
59 | parts/
60 | sdist/
61 | var/
62 | wheels/
63 | *.egg-info/
64 | .installed.cfg
65 | *.egg
66 |
67 | # PyInstaller
68 | *.manifest
69 | *.spec
70 |
71 | # Installer logs
72 | pip-log.txt
73 | pip-delete-this-directory.txt
74 |
75 | # Unit test / coverage reports
76 | htmlcov/
77 | .tox/
78 | .coverage
79 | .coverage.*
80 | .cache
81 | nosetests.xml
82 | coverage.xml
83 |
84 | # Flask stuff:
85 | instance/
86 | .webassets-cache
87 |
88 | # PyBuilder
89 | target/
90 |
91 | # Jupyter Notebook
92 | .ipynb_checkpoints
93 |
94 | # pyenv
95 | .python-version
96 |
97 | # Environments
98 | .env
99 | .venv
100 | env/
101 | venv/
102 | ENV/
103 |
104 | # documentation
105 | docs
106 |
107 | # others
108 | .nojekyll
109 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These owners will be the default owners for everything in the repo.
2 | * @GuillaumeDSM @Herklos
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | - Drakkar-Software
5 | patreon: # Replace with a single Patreon username
6 | open_collective: # Replace with a single Open Collective username
7 | ko_fi: # Replace with a single Ko-fi username
8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
10 | liberapay: # Replace with a single Liberapay username
11 | issuehunt: # Replace with a single IssueHunt username
12 | otechie: # Replace with a single Otechie username
13 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | ## Step 1: Have you search for this issue before posting it?
8 |
9 | If you have discovered a bug in the bot, please [search our issue tracker](https://github.com/Drakkar-Software/OctoBot/issues?q=is%3Aissue).
10 | If it hasn't been reported, please create a new issue.
11 |
12 | ## Step 2: Describe your environment
13 | * OS : [Windows, Ubuntu, Debian, Raspbian...]
14 | * Python Version: _____ (`python -V`)
15 | * In case you are not using a binary version:
16 | * Branch: Master | Dev
17 | * Last Commit ID: _____ (`git log --format="%H" -n 1`)
18 |
19 | ## Step 3: Describe the problem:
20 | **Describe the bug**
21 | A clear and concise description of what the bug is.
22 |
23 | **Expected behavior**
24 | A clear and concise description of what you expected to happen.
25 |
26 | ### Steps to reproduce:
27 |
28 | 1. _____
29 | 2. _____
30 | 3. _____
31 |
32 | ### Observed Results:
33 |
34 | * What happened?
35 | * What did you expect to happen?
36 |
37 | ### Relevant code exceptions or logs:
38 | If applicable, add screenshots to help explain your problem.
39 |
40 | ```
41 | // paste your log here
42 | ```
43 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | - Add any other context or screenshots about the feature request here.
18 | - You can link a github gist to explain the feature
19 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Thank you for sending your pull request.
2 | But first, have you included unit tests, and is your code PEP8 conformant? [More details](https://github.com/Drakkar-Software/OctoBot/blob/dev/CONTRIBUTING.md)
3 |
4 | ## Summary
5 | Explain in one sentence the goal of this PR
6 |
7 | Solve the issue: #___
8 |
9 | ## Changelog
10 |
11 | - _____
12 | - _____
13 | - _____
14 |
15 | ## What's new?
16 | *Explain in details what this PR solve or improve.*
17 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | 2.0.x | :white_check_mark: |
8 | | < 2.0 | :x: |
9 |
10 | ## Reporting a Vulnerability
11 |
12 | We appreciate the efforts of security researchers and individuals who help us maintain the security of the software we create. If you believe you have found a security vulnerability, please allow us a reasonable amount of time to investigate and address the issue before making any information public.
13 |
14 | To report a security issue, please contact the OctoBot team at contact@octobot.cloud.
15 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: pip
4 | directory: "/"
5 | target-branch: "dev"
6 | schedule:
7 | interval: "weekly"
8 | open-pull-requests-limit: 10
9 | allow:
10 | - dependency-name: "OctoBot*"
11 | - dependency-name: "Async-Channel"
12 | - dependency-name: "cython"
13 | reviewers:
14 | - Herklos
15 | - GuillaumeDSM
16 | assignees:
17 | - Herklos
18 | - GuillaumeDSM
19 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: "OctoBot-Release"
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | release-jobs:
10 | name: Create release jobs
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout repository
15 | uses: actions/checkout@v4
16 |
17 | - name: Set Environment Variables
18 | run: |
19 | echo "VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV
20 |
21 | - name: Create release on OctoBot-Tentacles
22 | uses: actions/create-release@v1
23 | env:
24 | GITHUB_TOKEN: ${{ secrets.AUTH_TOKEN }}
25 | with:
26 | tag_name: ${{ env.VERSION }}
27 | release_name: Version - ${{ env.VERSION }}
28 | draft: false
29 | prerelease: false
30 | commitish: master
31 | repo: OctoBot-Tentacles
32 |
33 | # TODO uncomment when adding tests to OctoBot-Binary
34 | # - name: Wait for tentacles build done
35 | # uses: fountainhead/action-wait-for-check@v1.0.0
36 | # id: wait-for-build
37 | # with:
38 | # token: ${{ secrets.AUTH_TOKEN }}
39 | # checkName: "Done"
40 | # ref: ${{ github.sha }}
41 | # timeoutSeconds: 3600
42 | #
43 | # - name: Trigger fail when Main failed
44 | # if: steps.wait-for-build.outputs.conclusion == 'failure'
45 | # run: exit 1
46 |
47 | - name: Create release on OctoBot-Binary
48 | uses: actions/create-release@v1
49 | env:
50 | GITHUB_TOKEN: ${{ secrets.AUTH_TOKEN }}
51 | with:
52 | tag_name: ${{ env.VERSION }}
53 | release_name: Version - ${{ env.VERSION }}
54 | draft: false
55 | prerelease: false
56 | commitish: master
57 | repo: OctoBot-Binary
58 |
59 | notify:
60 | name: Notify
61 | runs-on: ubuntu-latest
62 | needs:
63 | - release-jobs
64 | if: ${{ failure() }}
65 |
66 | steps:
67 | - name: Notify discord
68 | uses: sarisia/actions-status-discord@v1
69 | with:
70 | status: Failure
71 | webhook: ${{ secrets.DISCORD_GITHUB_WEBHOOK }}
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 | *.orig
6 |
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | .pytest_cache/
13 | env/
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | *.egg-info/
27 | .installed.cfg
28 | *.egg
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | # *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | .hypothesis/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # virtualenv
85 | .venv
86 | venv/
87 | ENV/
88 |
89 | # Spyder project settings
90 | .spyderproject
91 | .spyproject
92 |
93 | # Rope project settings
94 | .ropeproject
95 |
96 | # mkdocs documentation
97 | /site
98 |
99 | # mypy
100 | .mypy_cache/
101 |
102 | # IDE
103 | .vscode/
104 | .idea
105 | .gitpod.yml
106 |
107 | # Tentacles manager temporary files
108 | octobot/creator_temp/
109 | creator_temp/
110 |
111 | # Data
112 | backtesting/collector/data/
113 | backtesting/data/
114 |
115 | # Tentacles
116 | tentacles
117 | downloaded_temp_tentacles
118 |
119 | # User config
120 | user/
121 | temp_config.json
122 |
123 | *.csv
124 | *.ods
125 | *.c
126 | *.h
127 |
128 | # OctoBot logs
129 | logs
130 |
131 | # Debug
132 | cython_debug/
133 |
134 | # ssl
135 | letsencrypt/
136 |
137 | # dev env
138 | .env
139 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 |
2 | tasks:
3 | - name: Initialize OctoBot
4 | init: |
5 | cd OctoBot
6 | python3 -m pip install -Ur requirements.txt
7 | command: |
8 | python3 start.py
9 | ports:
10 | - port: 5001
11 | onOpen: open-preview
12 | name: OctoBot
13 | github:
14 | prebuilds:
15 | master: true
16 | branches: true
17 | pullRequests: true
18 | pullRequestsFromForks: true
19 | addCheck: true
20 | addComment: true
21 | addBadge: true
22 |
23 |
24 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at drakkar-software@protonmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribute to OctoBot
2 |
3 | Feel like OctoBot is missing a feature? We welcome your pull requests!
4 |
5 | Are you new to OctoBot? Here is an [overview of the software architecture](https://octobot.cloud/en/guides/octobot-developers-environment/architecture) and here is the [developer guide](https://octobot.cloud/en/guides/developers).
6 |
7 | ## Development philosophy
8 |
9 | OctoBot is multi strategy, multi exchange and multi cryptocurrency.
10 | - Any change specific to a strategy must be done in the specific code of this strategy, usually in a [tentacle](https://github.com/Drakkar-Software/OctoBot-tentacles).
11 | - Any change specific to an exchange must be done in the exchange's associated [tentacle](https://github.com/Drakkar-Software/OctoBot-tentacles).
12 | - Changes specific to a cryptocurrency or trading pair will be refused unless justified in the generic code.
13 |
14 | ## Contribution guidelines
15 |
16 | - Create your PR against the `dev` branch, not `master` on the [OctoBot](https://github.com/Drakkar-Software/OctoBot) and [Tentacles](https://github.com/Drakkar-Software/OctoBot-tentacles) repositories, create them against `master` on other repositories.
17 | - Change must be tested via associated pytest test(s) the `tests` directory.
18 |
19 | ## OctoBot additional coding style
20 |
21 | - Change must be PEP8 compliant (max-line-length = 120).
22 | - Use local variable only if it improves code clarity.
23 | - Always use generators and comprehension list instead of loops when possible.
24 | - Use `try ... except` instead of `if` statements when `if` is 99% `True`.
25 |
26 | ## Adding dependencies
27 |
28 | For security and maintenance reasons, additional dependencies could be added to OctoBot only in case they are necessary for the global system to operate.
29 | If your development requires an external dependency, please either:
30 | - Open an issue to discuss the integration of this dependency into the main code.
31 | - Make this dependency import optional in your code at import time so that the main OctoBot can still import your code and leave the reponsability to the user to install this dependency to use your code.
32 |
--------------------------------------------------------------------------------
/DELIVERY.md:
--------------------------------------------------------------------------------
1 | Octobot's versions
2 | ====================
3 | # Version structure
4 | ## R.D.MA_MI
5 | - R : Octobot's last final release number
6 | - D : development stage
7 | - 0 for alpha (status)
8 | - 1 for beta (status)
9 | - 2 for release candidate
10 | - 3 for (final) release
11 | - MA : last major release number
12 | - MI : last minor release number
13 |
14 | # Long version structure
15 | ## R.D.MA_MI-D_LONG
16 | The designations are the same as "Version structure"
17 |
18 | The only change is the D_LONG that corresponds to the development stage :
19 | - **alpha** for alpha (status)
20 | - **beta** for beta (status)
21 | - **release** for release candidate
22 |
23 | Octobot's versions tag
24 | ====================
25 | # Version tag
26 | To create a tag, use `git tag R.D.MA_MI-D_LONG` with the associated version (see *Long version structure*).
27 |
28 | Then push this tag with `git-push --tags`.
29 |
30 | How to create an Octobot's release
31 | ====================
32 | # Minor release or daily release
33 | - Increase the **MI** version number (*R.D.MA_MI*) in :
34 | - *config/cst.py* --> MINOR_VERSION
35 | - *README.md* --> OctoBot [R.D.MA_MI]
36 | - Update *docs/CHANGELOG.md*
37 | - Create a tag (see *Octobot's versions tag*)
38 |
39 | # Major release or weekly release
40 | - Increase the **MA** version number and reset to 0 the **MI** version number (*R.D.MA_MI*) in :
41 | - *config/cst.py* --> MINOR_VERSION
42 | - *README.md* --> OctoBot [R.D.MA_MI]
43 | - Update *docs/CHANGELOG.md*
44 | - Create a tag (see *Octobot's versions tag*)
45 |
46 | # New development stage release
47 | - Change the **D** and **R** according to development stage (*R.D.MA_MI*) in :
48 | - *config/cst.py* --> MINOR_VERSION
49 | - *README.md* --> OctoBot [R.D.MA_MI]
50 | - Reset the **MA** and **MI** version number (R.D.MA_MI) in :
51 | - *config/cst.py* --> MINOR_VERSION
52 | - *README.md* --> OctoBot [R.D.MA_MI]
53 | - Update *docs/CHANGELOG.md*
54 | - Create a tag (see *Octobot's versions tag*)
55 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim-buster AS base
2 |
3 | WORKDIR /
4 |
5 | # requires git to install requirements with git+https
6 | # Update to debian archive from https://gist.github.com/ishad0w/6ce1eb569c734880200c47923577426a
7 | RUN echo "deb http://archive.debian.org/debian buster main contrib non-free" > /etc/apt/sources.list \
8 | && echo "deb http://archive.debian.org/debian-security buster/updates main contrib non-free" >> /etc/apt/sources.list \
9 | && echo "deb http://archive.debian.org/debian buster-backports main contrib non-free" >> /etc/apt/sources.list \
10 | && apt-get update \
11 | && apt-get install -y --no-install-recommends build-essential git gcc binutils libffi-dev libssl-dev libxml2-dev libxslt1-dev libxslt-dev libjpeg62-turbo-dev zlib1g-dev \
12 | && python -m venv /opt/venv
13 |
14 | # skip cryptography rust compilation (required for armv7 builds)
15 | ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
16 |
17 | # Make sure we use the virtualenv:
18 | ENV PATH="/opt/venv/bin:$PATH"
19 |
20 | COPY . .
21 | RUN pip install -U setuptools wheel pip>=20.0.0 \
22 | && pip install --no-cache-dir --prefer-binary -r requirements.txt \
23 | && python setup.py install
24 |
25 | FROM python:3.10-slim-buster
26 |
27 | ARG TENTACLES_URL_TAG=""
28 | ENV TENTACLES_URL_TAG=$TENTACLES_URL_TAG
29 |
30 | WORKDIR /octobot
31 |
32 | # Import python dependencies
33 | COPY --from=base /opt/venv /opt/venv
34 |
35 | # Add default config files
36 | COPY octobot/config /octobot/octobot/config
37 |
38 | COPY docker/* /octobot/
39 |
40 | # 1. Install requirements
41 | # 2. Add cloudflare gpg key and add cloudflare repo in apt repositories (from https://pkg.cloudflare.com/index.html)
42 | # 3. Install required packages
43 | # 4. Finish env setup
44 | SHELL ["/bin/bash", "-o", "pipefail", "-c"]
45 | # Update to debian archive from https://gist.github.com/ishad0w/6ce1eb569c734880200c47923577426a
46 | RUN echo "deb http://archive.debian.org/debian buster main contrib non-free" > /etc/apt/sources.list \
47 | && echo "deb http://archive.debian.org/debian-security buster/updates main contrib non-free" >> /etc/apt/sources.list \
48 | && echo "deb http://archive.debian.org/debian buster-backports main contrib non-free" >> /etc/apt/sources.list \
49 | && apt-get update \
50 | && apt-get install -y --no-install-recommends curl \
51 | && mkdir -p /usr/share/keyrings \
52 | && chmod 0755 /usr/share/keyrings \
53 | && curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null \
54 | && echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared buster main' | tee /etc/apt/sources.list.d/cloudflared.list \
55 | && apt-get update \
56 | && apt-get install -y --no-install-recommends curl cloudflared libxslt-dev libxcb-xinput0 libjpeg62-turbo-dev zlib1g-dev libblas-dev liblapack-dev libatlas-base-dev libopenjp2-7 libtiff-dev \
57 | && rm -rf /var/lib/apt/lists/* \
58 | && ln -s /opt/venv/bin/OctoBot OctoBot # Make sure we use the virtualenv \
59 | && chmod +x docker-entrypoint.sh
60 |
61 | VOLUME /octobot/backtesting
62 | VOLUME /octobot/logs
63 | VOLUME /octobot/tentacles
64 | VOLUME /octobot/user
65 |
66 | EXPOSE 5001
67 |
68 | HEALTHCHECK --interval=15s --timeout=10s --retries=5 CMD curl -sS http://127.0.0.1:5001 || exit 1
69 |
70 | ENTRYPOINT ["./docker-entrypoint.sh"]
71 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | recursive-include octobot *.pxd
2 |
3 | include README.md
4 | include LICENSE
5 | include CHANGELOG.md
6 | include requirements.txt
7 |
8 | global-exclude *.c
9 |
--------------------------------------------------------------------------------
/additional_tests/exchanges_tests/.env.template:
--------------------------------------------------------------------------------
1 | PROXY_URL=
2 |
3 | BINANCE_KEY=
4 | BINANCE_SECRET=
5 |
6 | KUCOIN_READONLY_KEY=
7 | KUCOIN_READONLY_SECRET=
8 | KUCOIN_READONLY_PASSWORD=
9 |
10 | KUCOIN_KEY=
11 | KUCOIN_SECRET=
12 | KUCOIN_PASSWORD=
13 | KUCOIN_IS_PROXIED=true
14 |
15 | PHEMEX_KEY=
16 | PHEMEX_SECRET=
17 | PHEMEX_PASSWORD=
18 | PHEMEX_SANDBOXED=true
19 |
--------------------------------------------------------------------------------
/additional_tests/exchanges_tests/test_coinbase_ed25519.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import pytest
17 |
18 | import additional_tests.exchanges_tests.test_coinbase as test_coinbase
19 |
20 | # All test coroutines will be treated as marked.
21 | pytestmark = pytest.mark.asyncio
22 |
23 |
24 | class TestCoinbaseEd25519AuthenticatedExchange(test_coinbase.TestCoinbaseAuthenticatedExchange):
25 | # same as regular coinbase exchange test except that it is using the Ed25519 api key format
26 | # (after organizations/ and -----BEGIN EC PRIVATE)
27 | CREDENTIALS_EXCHANGE_NAME = "coinbase_ED25519"
28 |
--------------------------------------------------------------------------------
/additional_tests/historical_backend_tests/.env.template:
--------------------------------------------------------------------------------
1 | CLICKHOUSE_HOST=
2 | CLICKHOUSE_PORT=
3 | CLICKHOUSE_USERNAME=
4 | CLICKHOUSE_PASSWORD=
5 |
6 | ICEBERG_CATALOG_NAMESPACE=
7 | ICEBERG_CATALOG_WAREHOUSE=
8 | ICEBERG_CATALOG_NAME=
9 | ICEBERG_OHLCV_HISTORY_TABLE=
10 | ICEBERG_CATALOG_URI=
11 | ICEBERG_CATALOG_TOKEN=
12 | ICEBERG_S3_ACCESS_KEY=
13 | ICEBERG_S3_SECRET_KEY=
14 | ICEBERG_S3_REGION=
15 | ICEBERG_S3_ENDPOINT=
16 | CREATE_ICEBERG_DB_IF_MISSING=false
17 |
--------------------------------------------------------------------------------
/additional_tests/historical_backend_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import dotenv
17 | import pytest_asyncio
18 | import os
19 |
20 | LOADED_BACKEND_CREDS_ENV_VARIABLES = False
21 | def _load_historical_backend_creds_env_variables_if_necessary():
22 | global LOADED_BACKEND_CREDS_ENV_VARIABLES
23 | if not LOADED_BACKEND_CREDS_ENV_VARIABLES:
24 | # load environment variables from .env file if exists
25 | dotenv_path = os.getenv("HISTORICAL_BACKEND_TESTS_DOTENV_PATH", os.path.dirname(os.path.abspath(__file__)))
26 | dotenv.load_dotenv(os.path.join(dotenv_path, ".env"), verbose=False)
27 | LOADED_BACKEND_CREDS_ENV_VARIABLES = True
28 |
29 | # load it before octobot constants
30 | _load_historical_backend_creds_env_variables_if_necessary()
31 |
32 | import octobot.community
33 | import octobot.enums
34 |
35 |
36 | @pytest_asyncio.fixture
37 | async def clickhouse_client():
38 | async with octobot.community.history_backend_client(octobot.enums.CommunityHistoricalBackendType.Clickhouse) as client:
39 | yield client
40 |
41 |
42 | @pytest_asyncio.fixture
43 | async def iceberg_client():
44 | async with octobot.community.history_backend_client(octobot.enums.CommunityHistoricalBackendType.Iceberg) as client:
45 | yield client
46 |
--------------------------------------------------------------------------------
/additional_tests/supabase_backend_tests/.env.template:
--------------------------------------------------------------------------------
1 | SUPABASE_BACKEND_URL=
2 | SUPABASE_BACKEND_KEY=
3 |
4 | SUPABASE_BACKEND_CLIENT_1_EMAIL=
5 | SUPABASE_BACKEND_CLIENT_1_PASSWORD=
6 | SUPABASE_BACKEND_CLIENT_1_AUTH_KEY=
7 | SUPABASE_BACKEND_CLIENT_1_EXPIRED_JWT_TOKEN=
8 |
9 | SUPABASE_BACKEND_CLIENT_2_EMAIL=
10 | SUPABASE_BACKEND_CLIENT_2_PASSWORD=
11 |
12 | SUPABASE_BACKEND_CLIENT_3_EMAIL=
13 | SUPABASE_BACKEND_CLIENT_3_PASSWORD=
14 |
15 | SUPABASE_BACKEND_SERVICE_KEY=
16 |
--------------------------------------------------------------------------------
/additional_tests/supabase_backend_tests/test_cryptocurrencies.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import postgrest
17 | import pytest
18 |
19 | from additional_tests.supabase_backend_tests import authenticated_client_1
20 |
21 |
22 | # All test coroutines will be treated as marked.
23 | pytestmark = pytest.mark.asyncio
24 |
25 |
26 | async def test_paginated_fetch_cryptocurrencies(authenticated_client_1):
27 | pages = []
28 |
29 | def cryptocurrencies_request_factory(table: postgrest.AsyncRequestBuilder, select_count):
30 | pages.append(1)
31 | return (
32 | table.select(
33 | "symbol, last_price",
34 | count=select_count
35 | )
36 | )
37 |
38 | raw_currencies = await authenticated_client_1.paginated_fetch(
39 | authenticated_client_1,
40 | "cryptocurrencies",
41 | cryptocurrencies_request_factory
42 | )
43 | assert len(raw_currencies) > 1500
44 | # ensure at least 2 calls have been performed
45 | assert 2 <= sum(pages)
46 |
47 |
--------------------------------------------------------------------------------
/additional_tests/supabase_backend_tests/test_storage.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import json
17 | import pytest
18 |
19 | from additional_tests.supabase_backend_tests import admin_client
20 |
21 |
22 | # All test coroutines will be treated as marked.
23 | pytestmark = pytest.mark.asyncio
24 |
25 |
26 | async def test_upload_asset(admin_client):
27 | asset_content = json.dumps({"test_upload_asset_content": 1}).encode()
28 | asset_name = "test_upload_asset"
29 | asset_bucket = "product-images"
30 | await admin_client.remove_asset(asset_bucket, asset_name) # remove asset if exists
31 | uploaded_asset_path = await admin_client.upload_asset(asset_bucket, asset_name, asset_content)
32 |
33 | assets = await admin_client.list_assets(asset_bucket)
34 | asset_by_name = {
35 | asset["name"]: asset
36 | for asset in assets
37 | }
38 | assert uploaded_asset_path in asset_by_name
39 | assert asset_by_name[uploaded_asset_path]["name"] == asset_name
40 |
41 | await admin_client.remove_asset(asset_bucket, asset_name)
42 |
43 | assets = await admin_client.list_assets(asset_bucket)
44 | asset_by_name = {
45 | asset["name"]: asset
46 | for asset in assets
47 | }
48 | # asset is removed
49 | assert uploaded_asset_path not in asset_by_name
50 |
--------------------------------------------------------------------------------
/dev_requirements.txt:
--------------------------------------------------------------------------------
1 | pytest>=7.1
2 | pytest-asyncio>=0.19,<1.1.0 # 1.1.0 is not compatible with pytest-timeout (re-raise TimeoutError)
3 | pytest-cov
4 | pytest-timeout
5 |
6 | mock>=4.0.1
7 |
8 | coverage
9 | coveralls
10 |
11 | twine
12 | pip
13 | setuptools
14 | wheel
15 |
16 | pur
17 |
18 | pylint
19 |
--------------------------------------------------------------------------------
/docker-compose.https.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 | octobot:
5 | image: drakkarsoftware/octobot:stable
6 | restart: always
7 | labels:
8 | - traefik.enable=true
9 | - traefik.http.routers.octobot.rule=Host(`${HOST:-octobot.localhost}`)
10 | - traefik.http.services.octobot.loadbalancer.server.port=${PORT:-5001}
11 | - traefik.http.routers.octobot.tls=true
12 | - traefik.http.routers.octobot.tls.certresolver=letsencrypt
13 | volumes:
14 | - ./logs:/octobot/logs
15 | - ./backtesting:/octobot/backtesting
16 | - ./tentacles:/octobot/tentacles
17 | - ./user:/octobot/user
18 | expose:
19 | - ${PORT:-5001}
20 |
21 | traefik:
22 | image: traefik:v2
23 | restart: always
24 | command:
25 | - "--providers.docker=true"
26 | - "--providers.docker.exposedbydefault=false"
27 | - "--entrypoints.web.address=:80"
28 | - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
29 | - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
30 | - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
31 | - "--entrypoints.websecure.address=:443"
32 | - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL:-admin@example.com}"
33 | - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
34 | - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
35 | ports:
36 | - "80:80"
37 | - "443:443"
38 | volumes:
39 | - /var/run/docker.sock:/var/run/docker.sock:ro
40 | - ./letsencrypt:/letsencrypt
41 | environment:
42 | - LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL:-admin@example.com}
43 |
44 | watchtower:
45 | image: containrrr/watchtower
46 | restart: always
47 | command: --cleanup --include-restarting
48 | volumes:
49 | - /var/run/docker.sock:/var/run/docker.sock
50 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 |
3 | services:
4 | octobot:
5 | image: drakkarsoftware/octobot:stable
6 | volumes:
7 | - ./logs:/octobot/logs
8 | - ./backtesting:/octobot/backtesting
9 | - ./tentacles:/octobot/tentacles
10 | - ./user:/octobot/user
11 | ports:
12 | - ${PORT:-80}:${PORT:-5001}
13 | restart: always
14 |
15 | watchtower:
16 | image: containrrr/watchtower
17 | restart: always
18 | command: --cleanup --include-restarting
19 | volumes:
20 | - /var/run/docker.sock:/var/run/docker.sock
21 |
--------------------------------------------------------------------------------
/docker/README.md:
--------------------------------------------------------------------------------
1 | # Docker
2 | ## Install docker & docker compose
3 | ```
4 | curl -fsSL https://get.docker.com -o get-docker.sh
5 | sh get-docker.sh
6 | ```
7 |
8 | ## Docker compose setup
9 | ### With SSL
10 | - Create a .env file
11 | ```
12 | HOST=your-domain.com
13 | LETSENCRYPT_EMAIL=contact@your-domain.com
14 | ```
15 |
16 | ### Start
17 | ```
18 | docker compose up -d
19 | ```
20 |
--------------------------------------------------------------------------------
/docker/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Exit immediately if a command exits with a non-zero status
4 | set -e
5 |
6 | # Save OctoBot config
7 | if [[ -n "${OCTOBOT_CONFIG}" ]]; then
8 | echo "$OCTOBOT_CONFIG" | tee /octobot/user/config.json >/dev/null
9 | fi
10 |
11 | # Start cloudflared tunnel
12 | bash tunnel.sh
13 |
14 | # Disable set -e
15 | set +e
16 |
17 | # Start OctoBot
18 | ./OctoBot
19 |
--------------------------------------------------------------------------------
/docker/tunnel.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | export TUNNEL_LOGFILE=/root/cloudflared.log
4 | export TUNNEL_LOGLEVEL=info
5 |
6 | # load env file
7 | ENV_FILE=.env
8 | if [[ -f "$ENV_FILE" ]]; then
9 | source $ENV_FILE
10 | fi
11 |
12 | # start cloudflared if token is provided
13 | # https://developers.cloudflare.com/cloudflare-one/tutorials/cli/
14 | if [[ -n "$CLOUDFLARE_TOKEN" ]]; then
15 | cloudflared tunnel --url http://localhost:5001 --no-autoupdate run --token $CLOUDFLARE_TOKEN &
16 | fi
17 |
--------------------------------------------------------------------------------
/octobot.hcl:
--------------------------------------------------------------------------------
1 | job "octobot" {
2 | type = "service"
3 |
4 | group "octobot" {
5 | count = 1
6 |
7 | restart {
8 | attempts = 0
9 | mode = "fail"
10 | }
11 |
12 | task "octobot" {
13 | driver = "docker"
14 |
15 | config {
16 | image = "drakkarsoftware/octobot:stable"
17 |
18 | ports = ["http"]
19 |
20 | # you should use a CSI or NFS instead of a volume
21 | volumes = [
22 | "./logs:/octobot/logs",
23 | "./backtesting:/octobot/backtesting",
24 | "./tentacles:/octobot/tentacles",
25 | "./user:/octobot/user"
26 | ]
27 | }
28 |
29 | env {
30 | PORT = 5001
31 | }
32 |
33 | resources {
34 | cpu = 500 # MHz
35 | memory = 512 # MB
36 | }
37 | }
38 |
39 | network {
40 | port "http" {
41 | static = 80
42 | to = 5001
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/octobot/README.md:
--------------------------------------------------------------------------------
1 | # OctoBot source code
2 |
3 | In this repository is containing the highest abstraction level code of OctoBot.
4 |
5 | - The trading logic code can be found on the [OctoBot Trading dependency](https://github.com/Drakkar-Software/OctoBot-trading)
6 | - OctoBot strategies, external connectors and UI are located in the [OctoBot Tentacles](https://github.com/Drakkar-Software/OctoBot-tentacles) repository
7 |
8 | More info on OctoBot's architecture on the [OctoBot website](https://www.octobot.cloud/en/guides/octobot-developers-environment/architecture?utm_source=github&utm_medium=dk&utm_campaign=regular_open_source_content&utm_content=architecture)
9 |
--------------------------------------------------------------------------------
/octobot/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | PROJECT_NAME = "OctoBot"
18 | AUTHOR = "Drakkar-Software"
19 | VERSION = "2.0.14" # major.minor.revision
20 | LONG_VERSION = f"{VERSION}"
21 |
--------------------------------------------------------------------------------
/octobot/api/updater.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | import octobot.updater.updater_factory as updater_factory
18 |
19 |
20 | def get_updater():
21 | return updater_factory.create_updater()
22 |
--------------------------------------------------------------------------------
/octobot/automation/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 |
18 | from octobot.automation import bases
19 | from octobot.automation.bases import (
20 | AbstractAction,
21 | AbstractCondition,
22 | AbstractTriggerEvent,
23 | AutomationStep,
24 | )
25 |
26 |
27 | from octobot.automation import automation
28 | from octobot.automation.automation import (
29 | Automation,
30 | )
31 |
32 |
33 | __all__ = [
34 | "AbstractAction",
35 | "AbstractCondition",
36 | "AbstractTriggerEvent",
37 | "AutomationStep",
38 | "Automation",
39 | ]
40 |
--------------------------------------------------------------------------------
/octobot/automation/bases/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.automation.bases import abstract_action
18 |
19 | from octobot.automation.bases.abstract_action import (
20 | AbstractAction,
21 | )
22 |
23 | from octobot.automation.bases import abstract_condition
24 |
25 | from octobot.automation.bases.abstract_condition import (
26 | AbstractCondition,
27 | )
28 |
29 | from octobot.automation.bases import abstract_trigger_event
30 |
31 | from octobot.automation.bases.abstract_trigger_event import (
32 | AbstractTriggerEvent,
33 | )
34 |
35 | from octobot.automation.bases import automation_step
36 |
37 | from octobot.automation.bases.automation_step import (
38 | AutomationStep,
39 | )
40 |
41 | __all__ = [
42 | "AbstractAction",
43 | "AbstractCondition",
44 | "AbstractTriggerEvent",
45 | "AutomationStep",
46 | ]
47 |
--------------------------------------------------------------------------------
/octobot/automation/bases/abstract_action.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import abc
17 |
18 | import octobot.automation.bases.automation_step as automation_step
19 |
20 |
21 | class AbstractAction(automation_step.AutomationStep, abc.ABC):
22 | async def process(self):
23 | raise NotImplementedError
24 |
--------------------------------------------------------------------------------
/octobot/automation/bases/abstract_condition.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import abc
17 |
18 | import octobot.automation.bases.automation_step as automation_step
19 |
20 |
21 | class AbstractCondition(automation_step.AutomationStep, abc.ABC):
22 | async def evaluate(self) -> bool:
23 | raise NotImplementedError
24 |
--------------------------------------------------------------------------------
/octobot/automation/bases/abstract_trigger_event.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import abc
17 | import time
18 |
19 | import octobot.automation.bases.automation_step as automation_step
20 |
21 |
22 | class AbstractTriggerEvent(automation_step.AutomationStep, abc.ABC):
23 | def __init__(self):
24 | super(AbstractTriggerEvent, self).__init__()
25 | self.should_stop = False
26 | self.trigger_only_once = False
27 | self.max_trigger_frequency = 0
28 | self._last_trigger_time = 0
29 |
30 | async def stop(self):
31 | self.should_stop = True
32 |
33 | async def _get_next_event(self):
34 | raise NotImplementedError
35 |
36 | async def next_event(self):
37 | """
38 | Async generator, use as follows:
39 | async for event in self.next_event():
40 | # triggered when an event occurs
41 | """
42 | self._last_trigger_time = 0
43 | while not self.should_stop and not (self.trigger_only_once and self._last_trigger_time != 0):
44 | new_event = await self._get_next_event()
45 | trigger_time = time.time()
46 | if not self.max_trigger_frequency or (trigger_time - self._last_trigger_time > self.max_trigger_frequency):
47 | yield new_event
48 | self._last_trigger_time = time.time()
49 |
--------------------------------------------------------------------------------
/octobot/automation/bases/automation_step.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot_commons.logging as logging
17 | import octobot_commons.configuration as configuration
18 |
19 |
20 | class AutomationStep:
21 | def __init__(self):
22 | self.logger = logging.get_logger(self.get_name())
23 |
24 | @classmethod
25 | def get_name(cls):
26 | return cls.__name__
27 |
28 | @staticmethod
29 | def get_description() -> str:
30 | raise NotImplementedError
31 |
32 | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
33 | raise NotImplementedError
34 |
35 | def apply_config(self, config):
36 | raise NotImplementedError
37 |
--------------------------------------------------------------------------------
/octobot/backtesting/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.backtesting import abstract_backtesting_test
18 | from octobot.backtesting import independent_backtesting
19 | from octobot.backtesting import octobot_backtesting
20 | from octobot.backtesting.abstract_backtesting_test import (
21 | AbstractBacktestingTest,
22 | )
23 | from octobot.backtesting.independent_backtesting import (
24 | IndependentBacktesting,
25 | )
26 | from octobot.backtesting.octobot_backtesting import (
27 | OctoBotBacktesting,
28 | )
29 |
30 | __all__ = [
31 | "OctoBotBacktesting",
32 | "IndependentBacktesting",
33 | "AbstractBacktestingTest",
34 | ]
35 |
--------------------------------------------------------------------------------
/octobot/channels/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.channels import octobot_channel
18 |
19 | from octobot.channels.octobot_channel import (
20 | OctoBotChannelConsumer,
21 | OctoBotChannelProducer,
22 | OctoBotChannel,
23 | )
24 |
25 | __all__ = [
26 | "OctoBotChannelConsumer",
27 | "OctoBotChannelProducer",
28 | "OctoBotChannel",
29 | ]
30 |
--------------------------------------------------------------------------------
/octobot/community/errors.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot_commons.authentication as commons_authentication
17 |
18 |
19 | class RequestError(Exception):
20 | pass
21 |
22 |
23 | class StatusCodeRequestError(RequestError):
24 | pass
25 |
26 |
27 | class SessionTokenExpiredError(commons_authentication.AuthenticationError):
28 | pass
29 |
30 |
31 | class JWTExpiredError(commons_authentication.AuthenticationError):
32 | pass
33 |
34 |
35 | class BotError(commons_authentication.UnavailableError):
36 | pass
37 |
38 |
39 | class BotNotFoundError(BotError):
40 | pass
41 |
42 |
43 | class BotDeploymentURLNotFoundError(BotError):
44 | pass
45 |
46 |
47 | class MissingBotConfigError(BotError):
48 | pass
49 |
50 |
51 | class InvalidBotConfigError(BotError):
52 | pass
53 |
54 |
55 | class MissingProductConfigError(BotError):
56 | pass
57 |
58 |
59 | class EmailValidationRequiredError(commons_authentication.AuthenticationError):
60 | pass
61 |
62 |
63 | class NoBotDeviceError(BotError):
64 | pass
65 |
66 |
67 | class ExtensionRequiredError(Exception):
68 | pass
69 |
--------------------------------------------------------------------------------
/octobot/community/errors_upload/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.community.errors_upload import sentry_tracker
18 | from octobot.community.errors_upload.sentry_tracker import (
19 | init_sentry_tracker,
20 | flush_tracker,
21 | )
22 |
23 | __all__ = [
24 | "init_sentry_tracker",
25 | "flush_tracker",
26 | ]
27 |
--------------------------------------------------------------------------------
/octobot/community/feeds/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.community.feeds import abstract_feed
18 | from octobot.community.feeds.abstract_feed import (
19 | AbstractFeed,
20 | )
21 | from octobot.community.feeds import community_ws_feed
22 | from octobot.community.feeds.community_ws_feed import (
23 | CommunityWSFeed,
24 | )
25 | from octobot.community.feeds import community_mqtt_feed
26 | from octobot.community.feeds.community_mqtt_feed import (
27 | CommunityMQTTFeed,
28 | )
29 | from octobot.community.feeds import community_supabase_feed
30 | from octobot.community.feeds.community_supabase_feed import (
31 | CommunitySupabaseFeed,
32 | )
33 | from octobot.community.feeds import feed_factory
34 | from octobot.community.feeds.feed_factory import (
35 | community_feed_factory,
36 | )
37 |
38 | __all__ = [
39 | "AbstractFeed",
40 | "CommunityWSFeed",
41 | "CommunityMQTTFeed",
42 | "CommunitySupabaseFeed",
43 | "community_feed_factory",
44 | ]
45 |
--------------------------------------------------------------------------------
/octobot/community/feeds/abstract_feed.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import time
17 | import typing
18 |
19 | import octobot.enums as enums
20 | import octobot_commons.logging as bot_logging
21 |
22 |
23 | class AbstractFeed:
24 | def __init__(self, feed_url, authenticator):
25 | self.logger: bot_logging.BotLogger = bot_logging.get_logger(
26 | self.__class__.__name__
27 | )
28 | self.feed_url = feed_url
29 | self.should_stop = False
30 | self.authenticator = authenticator
31 | self.feed_callbacks = {}
32 | self.subscribed = False
33 | self.last_message_time = None
34 | self.is_signal_receiver = False
35 | self.is_signal_emitter = False
36 |
37 | def has_registered_feed(self) -> bool:
38 | return bool(self.feed_callbacks)
39 |
40 | async def start(self, stop_on_cfg_action: typing.Optional[enums.CommunityConfigurationActions]):
41 | raise NotImplementedError("start is not implemented")
42 |
43 | async def stop(self):
44 | raise NotImplementedError("stop is not implemented")
45 |
46 | async def register_feed_callback(self, channel_type, callback, identifier=None):
47 | raise NotImplementedError("register_feed_callback is not implemented")
48 |
49 | async def send(self, message, channel_type, identifier, **kwargs):
50 | raise NotImplementedError("send is not implemented")
51 |
52 | def can_connect(self):
53 | return True
54 |
55 | def is_connected_to_remote_feed(self):
56 | return False
57 |
58 | def update_last_message_time(self):
59 | self.last_message_time = time.time()
60 |
61 | def is_up_to_date_with_account(self, user_account):
62 | return True
63 |
64 | def is_connected(self):
65 | return False
66 |
--------------------------------------------------------------------------------
/octobot/community/feeds/feed_factory.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot.enums
17 | import octobot.constants
18 | import octobot.community.feeds.community_ws_feed as community_ws_feed
19 | import octobot.community.feeds.community_mqtt_feed as community_mqtt_feed
20 | import octobot.community.feeds.community_supabase_feed as community_supabase_feed
21 |
22 |
23 | def community_feed_factory(authenticator, feed_type: octobot.enums.CommunityFeedType):
24 | feed_url = octobot.constants.COMMUNITY_FEED_URL
25 | if feed_type is octobot.enums.CommunityFeedType.WebsocketFeed:
26 | return community_ws_feed.CommunityWSFeed(feed_url, authenticator)
27 | if feed_type is octobot.enums.CommunityFeedType.MQTTFeed:
28 | return community_mqtt_feed.CommunityMQTTFeed(feed_url, authenticator)
29 | if feed_type is octobot.enums.CommunityFeedType.SupabaseFeed:
30 | return community_supabase_feed.CommunitySupabaseFeed(feed_url, authenticator)
31 | raise NotImplementedError(f"Unsupported feed type: {feed_type}")
32 |
--------------------------------------------------------------------------------
/octobot/community/history_backend/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.community.history_backend import history_backend_factory
18 | from octobot.community.history_backend.history_backend_factory import (
19 | history_backend_client,
20 | )
21 |
22 | from octobot.community.history_backend import historical_backend_client
23 | from octobot.community.history_backend.historical_backend_client import (
24 | HistoricalBackendClient,
25 | )
26 |
27 | from octobot.community.history_backend import clickhouse_historical_backend_client
28 | from octobot.community.history_backend.clickhouse_historical_backend_client import (
29 | ClickhouseHistoricalBackendClient,
30 | )
31 |
32 | from octobot.community.history_backend import iceberg_historical_backend_client
33 | from octobot.community.history_backend.iceberg_historical_backend_client import (
34 | IcebergHistoricalBackendClient,
35 | )
36 |
37 | __all__ = [
38 | "history_backend_client",
39 | "HistoricalBackendClient",
40 | "ClickhouseHistoricalBackendClient",
41 | "IcebergHistoricalBackendClient",
42 | ]
43 |
--------------------------------------------------------------------------------
/octobot/community/history_backend/historical_backend_client.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import typing
17 | import octobot_commons.enums as commons_enums
18 |
19 |
20 | class HistoricalBackendClient:
21 | """Abstract base class for historical data backend clients"""
22 |
23 | async def open(self):
24 | raise NotImplementedError("open is not implemented")
25 |
26 | async def close(self):
27 | raise NotImplementedError("close is not implemented")
28 |
29 | async def fetch_candles_history(
30 | self,
31 | exchange: str,
32 | symbol: str,
33 | time_frame: commons_enums.TimeFrames,
34 | first_open_time: float,
35 | last_open_time: float
36 | ) -> list[list[float]]:
37 | raise NotImplementedError("fetch_candles_history is not implemented")
38 |
39 | async def fetch_extended_candles_history(
40 | self,
41 | exchange: str,
42 | symbols: list[str],
43 | time_frames: list[commons_enums.TimeFrames],
44 | first_open_time: typing.Optional[float] = None,
45 | last_open_time: typing.Optional[float] = None,
46 | ) -> list[list[typing.Union[float, str]]]:
47 | raise NotImplementedError("fetch_extended_candles_history is not implemented")
48 |
49 | async def fetch_candles_history_range(
50 | self,
51 | exchange: str,
52 | symbol: str,
53 | time_frame: commons_enums.TimeFrames
54 | ) -> tuple[float, float]:
55 | raise NotImplementedError("fetch_candles_history_range is not implemented")
56 |
57 | async def insert_candles_history(self, rows: list, column_names: list) -> None:
58 | raise NotImplementedError("insert_candles_history is not implemented")
59 |
60 | async def fetch_all_candles_for_exchange(self, exchange: str) -> list[list[float]]:
61 | raise NotImplementedError("fetch_all_candles_for_exchange is not implemented")
62 |
63 | @staticmethod
64 | def get_formatted_time(timestamp: float):
65 | raise NotImplementedError("get_formatted_time is not implemented")
66 |
--------------------------------------------------------------------------------
/octobot/community/history_backend/history_backend_factory.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import contextlib
17 |
18 | import octobot.community.history_backend.clickhouse_historical_backend_client as clickhouse_historical_backend_client
19 | import octobot.community.history_backend.iceberg_historical_backend_client as iceberg_historical_backend_client
20 | import octobot.enums
21 |
22 |
23 | @contextlib.asynccontextmanager
24 | async def history_backend_client(
25 | backend_type: octobot.enums.CommunityHistoricalBackendType = octobot.enums.CommunityHistoricalBackendType.DEFAULT,
26 | **kwargs
27 | ):
28 | client = _create_client(backend_type, **kwargs)
29 | try:
30 | await client.open()
31 | yield client
32 | finally:
33 | await client.close()
34 |
35 | def _create_client(
36 | backend_type: octobot.enums.CommunityHistoricalBackendType = octobot.enums.CommunityHistoricalBackendType.DEFAULT,
37 | **kwargs
38 | ):
39 | """
40 | Usage:
41 | async with history_backend_client(backend_type) as client:
42 | await client.xxxx()
43 | """
44 | if backend_type is octobot.enums.CommunityHistoricalBackendType.Iceberg:
45 | return iceberg_historical_backend_client.IcebergHistoricalBackendClient(**kwargs)
46 | if backend_type is octobot.enums.CommunityHistoricalBackendType.Clickhouse:
47 | return clickhouse_historical_backend_client.ClickhouseHistoricalBackendClient(**kwargs)
48 | raise NotImplementedError(f"Unsupported historical backend type: {backend_type}")
49 |
--------------------------------------------------------------------------------
/octobot/community/history_backend/util.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 |
4 | def get_utc_timestamp_from_datetime(dt: datetime.datetime) -> float:
5 | """
6 | Convert a datetime to a timestamp in UTC
7 | WARNING: usable here as we know this DB stores time in UTC only
8 | """
9 | return dt.replace(tzinfo=datetime.timezone.utc).timestamp()
10 |
11 |
12 | def deduplicate(elements, keys: list) -> list:
13 | # from https://stackoverflow.com/questions/480214/how-do-i-remove-duplicates-from-a-list-while-preserving-order
14 | seen = set()
15 | seen_add = seen.add
16 | elements_and_signature = (
17 | (element, "".join(str(element[key]) for key in keys))
18 | for element in elements
19 | )
20 | return [x for x, s in elements_and_signature if not (s in seen or seen_add(s))]
21 |
--------------------------------------------------------------------------------
/octobot/community/identifiers_provider.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot.constants as constants
17 | import octobot.enums as enums
18 | import octobot_commons.logging as logging
19 | import octobot_commons.configuration as configuration
20 |
21 |
22 | class IdentifiersProvider:
23 | ENABLED_ENVIRONMENT: str = None
24 | COMMUNITY_API_URL: str = None
25 | COMMUNITY_URL: str = None
26 | FRONTEND_PASSWORD_RECOVER_URL: str = None
27 | BACKEND_URL: str = None
28 | BACKEND_KEY: str = None
29 |
30 | @staticmethod
31 | def use_production():
32 | IdentifiersProvider.COMMUNITY_URL = constants.OCTOBOT_COMMUNITY_URL
33 | IdentifiersProvider.COMMUNITY_API_URL = constants.OCTOBOT_COMMUNITY_API_URL
34 | IdentifiersProvider.FRONTEND_PASSWORD_RECOVER_URL = constants.OCTOBOT_COMMUNITY_RECOVER_PASSWORD_URL
35 | IdentifiersProvider.BACKEND_URL = constants.COMMUNITY_BACKEND_URL
36 | IdentifiersProvider.BACKEND_KEY = constants.COMMUNITY_BACKEND_KEY
37 | IdentifiersProvider._register_environment(enums.CommunityEnvironments.Production)
38 |
39 | @staticmethod
40 | def use_staging():
41 | IdentifiersProvider.COMMUNITY_URL = constants.STAGING_OCTOBOT_COMMUNITY_URL
42 | IdentifiersProvider.COMMUNITY_API_URL = constants.STAGING_OCTOBOT_COMMUNITY_API_URL
43 | IdentifiersProvider.FRONTEND_PASSWORD_RECOVER_URL = constants.STAGING_COMMUNITY_RECOVER_PASSWORD_URL
44 | IdentifiersProvider.BACKEND_URL = constants.STAGING_COMMUNITY_BACKEND_URL
45 | IdentifiersProvider.BACKEND_KEY = constants.STAGING_COMMUNITY_BACKEND_KEY
46 | IdentifiersProvider._register_environment(enums.CommunityEnvironments.Staging)
47 |
48 | @staticmethod
49 | def _register_environment(env):
50 | if IdentifiersProvider.ENABLED_ENVIRONMENT != env:
51 | logging.get_logger(IdentifiersProvider.__name__).debug(f"Using {env.value} Community environment.")
52 | IdentifiersProvider.ENABLED_ENVIRONMENT = env
53 |
54 | @staticmethod
55 | def use_default():
56 | if constants.USE_BETA_EARLY_ACCESS:
57 | IdentifiersProvider.use_staging()
58 | else:
59 | IdentifiersProvider.use_production()
60 |
61 | @staticmethod
62 | def is_staging_environment_enabled(config: dict):
63 | try:
64 | env = config[constants.CONFIG_COMMUNITY][constants.CONFIG_COMMUNITY_ENVIRONMENT]
65 | return enums.CommunityEnvironments(env) is enums.CommunityEnvironments.Staging
66 | except (KeyError, ValueError):
67 | return False
68 |
69 | @staticmethod
70 | def use_environment_from_config(config: configuration.Configuration):
71 | if IdentifiersProvider.is_staging_environment_enabled(config.config):
72 | IdentifiersProvider.use_staging()
73 | else:
74 | IdentifiersProvider.use_default()
75 |
--------------------------------------------------------------------------------
/octobot/community/models/community_donation.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 |
18 | class CommunityDonation:
19 | def __init__(self, amount: str, currency: str, blockchain: str, transaction_id: str, address_to: str):
20 | self.amount = amount
21 | self.currency = currency
22 | self.blockchain = blockchain
23 | self.transaction_id = transaction_id
24 | self.address_to = address_to
25 |
26 | def __str__(self):
27 | return f"{self.amount} {self.currency} on {self.blockchain} ({self.transaction_id})"
28 |
29 | @staticmethod
30 | def from_community_dict(data):
31 | data_attributes = data.get("attributes", {})
32 | return CommunityDonation(
33 | data_attributes.get("amount"),
34 | data_attributes.get("currency"),
35 | data_attributes.get("blockchain"),
36 | data_attributes.get("transaction_id"),
37 | data_attributes.get("address_to")
38 | )
39 |
--------------------------------------------------------------------------------
/octobot/community/models/community_fields.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import enum
17 |
18 |
19 | class CommunityFields(enum.Enum):
20 | ID = "_id"
21 | CURRENT_SESSION = "currentsession"
22 | STARTED_AT = "startedat"
23 | UP_TIME = "uptime"
24 | VERSION = "version"
25 | SIMULATOR = "simulator"
26 | TRADER = "trader"
27 | EVAL_CONFIG = "evalconfig"
28 | PAIRS = "pairs"
29 | EXCHANGES = "exchanges"
30 | EXCHANGE_TYPES = "exchangetypes"
31 | NOTIFICATIONS = "notifications"
32 | TYPE = "type"
33 | PLATFORM = "platform"
34 | REFERENCE_MARKET = "referencemarket"
35 | PORTFOLIO_VALUE = "portfoliovalue"
36 | PROFITABILITY = "profitability"
37 | TRADED_VOLUMES = "tradedvolumes"
38 | SUPPORTS = "supports"
39 | ROLES = "roles"
40 | DONATIONS = "donations"
41 | SIGNAL_EMITTER = "signalemitter"
42 | SIGNAL_RECEIVER = "signalreceiver"
43 | COMMUNITY_BOT_TYPE = "communitybottype"
44 | PROFILE_NAME = "profilename"
45 | PROFILE_ID = "profileid"
46 | PROFILE_IMPORTED = "profileimported"
47 |
--------------------------------------------------------------------------------
/octobot/community/models/community_public_data.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import dataclasses
17 | import octobot.community.supabase_backend.enums as enums
18 | import octobot.community.models.strategy_data as strategy_data
19 |
20 |
21 | class CommunityPublicData:
22 | def __init__(self):
23 | self.products = _DataElement({}, False)
24 |
25 | def set_products(self, products):
26 | if products:
27 | self.products.value = {product[enums.ProductKeys.ID.value]: product for product in products}
28 | self.products.fetched = True
29 |
30 | def get_product_slug(self, product_id):
31 | return self.products.value[product_id][enums.ProductKeys.SLUG.value]
32 |
33 | def get_strategies(self, strategy_categories) -> list[strategy_data.StrategyData]:
34 | return [
35 | strategy_data.StrategyData.from_dict(strategy_dict)
36 | for strategy_dict in self.products.value.values()
37 | if self._get_category_type(strategy_dict) in strategy_categories
38 | ]
39 |
40 | def _get_category_type(self, product: dict):
41 | category = product.get("category") or {}
42 | return category.get("type")
43 |
44 | def get_strategy(self, strategy_id: str) -> strategy_data.StrategyData:
45 | return strategy_data.StrategyData.from_dict(self.products.value[strategy_id])
46 |
47 |
48 | @dataclasses.dataclass
49 | class _DataElement:
50 | value: any
51 | fetched: bool
52 |
--------------------------------------------------------------------------------
/octobot/community/models/community_supports.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot.community.models.community_donation as community_donation
17 | import octobot_commons.support as support
18 |
19 |
20 | class CommunitySupports(support.Support):
21 | DEFAULT_SUPPORT_ROLE = "default"
22 | OCTOBOT_DONOR_ROLE = "donor"
23 |
24 | def __init__(self, support_role: str = None, donations: list = None):
25 | self.support_role = support_role or CommunitySupports.DEFAULT_SUPPORT_ROLE
26 | self.donations = donations or []
27 |
28 | def is_supporting(self) -> bool:
29 | return self.support_role != self.DEFAULT_SUPPORT_ROLE or self.is_donor()
30 |
31 | def is_donor(self) -> bool:
32 | return self.support_role == self.OCTOBOT_DONOR_ROLE or bool(self.donations)
33 |
34 | @staticmethod
35 | def from_community_dict(data):
36 | return CommunitySupports(
37 | data["data"]["attributes"].get("support_role", CommunitySupports.DEFAULT_SUPPORT_ROLE),
38 | [community_donation.CommunityDonation.from_community_dict(donation_data)
39 | for donation_data in data.get("included", [])]
40 | )
41 |
--------------------------------------------------------------------------------
/octobot/community/models/community_tentacles_package.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import typing
17 | import packaging.version
18 |
19 | import octobot.community.identifiers_provider as identifiers_provider
20 | import octobot.constants as constants
21 |
22 |
23 | class CommunityTentaclesPackage:
24 | def __init__(self, name, description, url, activated, images, download_url, versions, last_version):
25 | self.name: str = name
26 | self.description: str = description
27 | self.url: str = url
28 | self.activated: bool = activated
29 | self.images: typing.List[str] = images
30 | self.download_url: str = download_url
31 | self.uninstalled: bool = not self.is_installed()
32 | self.versions: typing.List[str] = versions
33 | self.last_version: str = last_version
34 |
35 | @staticmethod
36 | def from_community_dict(data):
37 | data_attributes = data["attributes"]
38 | # todo update with new urls
39 | return CommunityTentaclesPackage(
40 | data_attributes.get("name"),
41 | data_attributes.get("description"),
42 | f"{identifiers_provider.IdentifiersProvider.COMMUNITY_URL}products/{data_attributes.get('product_slug')}",
43 | data_attributes.get("activated"),
44 | data["relationships"].get('images')['data'],
45 | f"{identifiers_provider.IdentifiersProvider.COMMUNITY_URL}{data_attributes.get('download_path')}",
46 | data_attributes.get("versions"),
47 | data_attributes.get("last_version")
48 | )
49 |
50 | def get_latest_compatible_version(self):
51 | current_bot_version = packaging.version.parse(constants.LONG_VERSION)
52 | if packaging.version.parse(self.last_version) <= current_bot_version:
53 | return self.last_version
54 | available_versions = sorted([packaging.version.parse(version) for version in self.versions], reverse=True)
55 | for version in available_versions:
56 | if version <= current_bot_version:
57 | return version
58 | return None
59 |
60 | def is_installed(self):
61 | #TODO tmp
62 | import random
63 | return random.choice((True, False))
64 |
--------------------------------------------------------------------------------
/octobot/community/models/executed_product_details.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import dataclasses
17 |
18 |
19 | @dataclasses.dataclass
20 | class ExecutedProductDetails:
21 | product_id: str
22 | started_at: float
23 |
--------------------------------------------------------------------------------
/octobot/community/models/startup_info.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 |
18 | class StartupInfo:
19 | FORCED_PROFILE_URL = "forced_profile_url"
20 | SUBSCRIBED_PRODUCTS_URLS = "subscribed_products_urls"
21 |
22 | def __init__(self, forced_profile_url, subscribed_products_urls):
23 | self.forced_profile_url = forced_profile_url
24 | self.subscribed_products_urls = subscribed_products_urls
25 |
26 | @staticmethod
27 | def from_dict(data):
28 | return StartupInfo(
29 | data.get(StartupInfo.FORCED_PROFILE_URL),
30 | [
31 | url
32 | for url in data.get(StartupInfo.SUBSCRIBED_PRODUCTS_URLS, []) or []
33 | if url # skip unset urls
34 | ]
35 | )
36 |
37 | def __str__(self):
38 | return f"forced_profile_url: {self.forced_profile_url}, " \
39 | f"subscribed_products_urls: {self.subscribed_products_urls}"
40 |
--------------------------------------------------------------------------------
/octobot/community/supabase_backend/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.community.supabase_backend import configuration_storage
18 | from octobot.community.supabase_backend.configuration_storage import (
19 | SyncConfigurationStorage,
20 | ASyncConfigurationStorage,
21 | )
22 | from octobot.community.supabase_backend import supabase_client
23 | from octobot.community.supabase_backend.supabase_client import (
24 | AuthenticatedAsyncSupabaseClient,
25 | )
26 | from octobot.community.supabase_backend import community_supabase_client
27 | from octobot.community.supabase_backend.community_supabase_client import (
28 | retried_failed_supabase_request,
29 | error_describer,
30 | CommunitySupabaseClient,
31 | HTTP_RETRY_COUNT,
32 | )
33 |
34 | __all__ = [
35 | "SyncConfigurationStorage",
36 | "ASyncConfigurationStorage",
37 | "AuthenticatedAsyncSupabaseClient",
38 | "retried_failed_supabase_request",
39 | "error_describer",
40 | "CommunitySupabaseClient",
41 | "HTTP_RETRY_COUNT",
42 | ]
43 |
--------------------------------------------------------------------------------
/octobot/community/supabase_backend/supabase_client.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import copy
17 | import typing
18 | import postgrest
19 | import supabase
20 |
21 |
22 | class AuthenticatedAsyncSupabaseClient(supabase.AClient):
23 | async def init_other_postgrest_client(
24 | self, supabase_url: str = None, supabase_key: str = None, schema: str = "public"
25 | ) -> postgrest.AsyncPostgrestClient:
26 | supabase_key = supabase_key or self.supabase_key
27 | options = copy.deepcopy(self.options)
28 | options.storage = None
29 | options.schema = schema
30 | if supabase_key != self.supabase_key:
31 | auth_headers = self._get_auth_headers(authorization=self._create_auth_header(supabase_key))
32 | # use local supabase key instead of self.supabase_key that is set by _get_auth_headers
33 | auth_headers["apiKey"] = supabase_key
34 | options.headers.update(auth_headers)
35 | return self._init_postgrest_client(
36 | rest_url=f"{supabase_url}/rest/v1" if supabase_url else self.rest_url,
37 | headers=options.headers,
38 | schema=options.schema,
39 | timeout=options.postgrest_client_timeout,
40 | )
41 |
42 | def remove_session_details(self):
43 | self.auth._remove_session()
44 | self.auth._notify_all_subscribers("SIGNED_OUT", None)
45 |
46 | async def aclose(self):
47 | # waiting from supabase close() method
48 | try:
49 | await self.auth.close()
50 | except RuntimeError:
51 | # can happen when "Event loop is closed"
52 | pass
53 | if self.realtime.is_connected:
54 | await self.realtime.close()
55 | if self._postgrest:
56 | try:
57 | await self._postgrest.aclose()
58 | except RuntimeError:
59 | # can happen when "Event loop is closed"
60 | pass
61 | # timer has to be stopped, there is no public stop api
62 | if self.auth._refresh_token_timer:
63 | self.auth._refresh_token_timer.cancel()
64 | self.auth._refresh_token_timer = None
--------------------------------------------------------------------------------
/octobot/config/config_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "DEBUG": {
5 | "type": "boolean"
6 | },
7 | "DEV-MODE": {
8 | "type": "boolean"
9 | },
10 | "SAVE_EVALUATIONS": {
11 | "type": "boolean"
12 | },
13 | "accepted_terms": {
14 | "type": "boolean"
15 | },
16 | "distribution": {
17 | "type": "string"
18 | },
19 | "profile": {
20 | "type": "string"
21 | },
22 | "community": {
23 | "type": "object",
24 | "properties": {
25 | "bot_id": {
26 | "type": "string"
27 | },
28 | "supabase.auth.token": {
29 | "type": "string"
30 | },
31 | "environment": {
32 | "type": "string"
33 | }
34 | }
35 | },
36 | "backtesting": {
37 | "type": "object",
38 | "properties": {
39 | "files": {
40 | "type": "array",
41 | "items": {
42 | "type": "string"
43 | }
44 | }
45 | }
46 | },
47 | "data-collector": {
48 | "type": "object",
49 | "properties": {
50 | "symbol": {
51 | "type": "string"
52 | }
53 | }
54 | },
55 | "exchanges": {
56 | "type": "object",
57 | "patternProperties": {
58 | "^.*quot;: {
59 | "type": "object"
60 | }
61 | }
62 | },
63 | "metrics": {
64 | "type": "object",
65 | "properties": {
66 | "enabled": {
67 | "type": "boolean"
68 | },
69 | "metrics-bot-id": {
70 | "type": "string"
71 | }
72 | }
73 | },
74 | "community-token": {
75 | "type": "string"
76 | },
77 | "notification": {
78 | "type": "object",
79 | "properties": {
80 | "global-info": {
81 | "type": "boolean"
82 | },
83 | "price-alerts": {
84 | "type": "boolean"
85 | },
86 | "trades": {
87 | "type": "boolean"
88 | },
89 | "other": {
90 | "type": "boolean"
91 | },
92 | "notification-type": {
93 | "type": "array",
94 | "items": {
95 | "type": "string"
96 | }
97 | }
98 | },
99 | "required": [
100 | "global-info",
101 | "price-alerts",
102 | "trades",
103 | "notification-type"
104 | ]
105 | },
106 | "services": {
107 | "type": "object",
108 | "patternProperties": {
109 | "^.*quot;: {
110 | "type": "object"
111 | }
112 | }
113 | },
114 | "watched_symbols": {
115 | "type": "array",
116 | "items": {
117 | "type": "string"
118 | }
119 | }
120 | },
121 | "required": [
122 | "backtesting",
123 | "exchanges",
124 | "services",
125 | "notification"
126 | ],
127 | "additionalProperties": {
128 | "type": "string"
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/octobot/config/default_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "backtesting": {
3 | "files": []
4 | },
5 | "exchanges": {},
6 | "services": {
7 | "web": {
8 | "auto-open-in-web-browser": true
9 | }
10 | },
11 | "notification":{
12 | "global-info": true,
13 | "price-alerts": true,
14 | "trades": true,
15 | "trading-script-alerts": true,
16 | "other": true,
17 | "notification-type": [
18 | "web"
19 | ]
20 | },
21 | "profile": "default",
22 | "accepted_terms": false,
23 | "distribution": "default"
24 | }
25 |
--------------------------------------------------------------------------------
/octobot/config/logging_config.ini:
--------------------------------------------------------------------------------
1 | [loggers]
2 | keys=root
3 |
4 | [handlers]
5 | keys=consoleHandler,fileHandler
6 |
7 | [formatters]
8 | keys=consoleFormatter,fileFormatter
9 |
10 | [logger_root]
11 | level=DEBUG
12 | handlers=consoleHandler,fileHandler
13 |
14 | [handler_consoleHandler]
15 | class=StreamHandler
16 | level=INFO
17 | formatter=consoleFormatter
18 | args=(sys.stdout,)
19 |
20 | [handler_fileHandler]
21 | class=handlers.RotatingFileHandler
22 | level=DEBUG
23 | formatter=fileFormatter
24 | args=('logs/OctoBot.log', 'a', 24000000, 20)
25 |
26 | [formatter_consoleFormatter]
27 | class=colorlog.ColoredFormatter
28 | format=%(log_color)s %(asctime)s %(levelname)-8s %(name)-20s %(message)s
29 |
30 | [formatter_fileFormatter]
31 | format=%(asctime)-16s %(levelname)-6s %(name)-20s %(filename)-s:%(lineno)-8s %(message)s
32 | datefmt=%Y-%m-%d %H:%M:%S
33 |
--------------------------------------------------------------------------------
/octobot/databases_util.py:
--------------------------------------------------------------------------------
1 | # Drakkar-Software OctoBot-Trading
2 | # Copyright (c) Drakkar-Software, All rights reserved.
3 | #
4 | # This library is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU Lesser General Public
6 | # License as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # This library is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # Lesser General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Lesser General Public
15 | # License along with this library
16 | import octobot_trading.api as trading_api
17 | import octobot_commons.databases as databases
18 | import octobot_commons.optimization_campaign as optimization_campaign
19 | import octobot_commons.constants as commons_constants
20 | import octobot_commons.errors as commons_errors
21 |
22 |
23 | def get_run_databases_identifier(config, tentacles_setup_config, trading_mode_class=None, enable_storage=True):
24 | trading_mode = commons_constants.DEFAULT_STORAGE_TRADING_MODE
25 | try:
26 | trading_mode = trading_mode_class or trading_api.get_activated_trading_mode(tentacles_setup_config)
27 | except commons_errors.ConfigTradingError:
28 | # use default value
29 | pass
30 | return databases.RunDatabasesIdentifier(
31 | trading_mode,
32 | optimization_campaign.OptimizationCampaign.get_campaign_name(tentacles_setup_config),
33 | backtesting_id=config.get(commons_constants.CONFIG_BACKTESTING_ID),
34 | optimizer_id=config.get(commons_constants.CONFIG_OPTIMIZER_ID),
35 | live_id=trading_api.get_current_bot_live_id(config),
36 | enable_storage=enable_storage
37 | )
38 |
--------------------------------------------------------------------------------
/octobot/disclaimer.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 |
18 | DISCLAIMER = [
19 | "Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL "
20 | "AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.",
21 | "Always start by running a trading bot in simulation mode and do not engage money before you understand "
22 | "how it works and what profit/loss you should expect.",
23 | "Do not hesitate to read the source code and understand the mechanism of this bot."
24 | ]
25 |
--------------------------------------------------------------------------------
/octobot/enums.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import enum
17 |
18 |
19 | class CommunityFeedType(enum.Enum):
20 | WebsocketFeed = "WebsocketFeed"
21 | MQTTFeed = "MQTTFeed"
22 | SupabaseFeed = "SupabaseFeed"
23 |
24 |
25 | class CommunityHistoricalBackendType(enum.Enum):
26 | Clickhouse = "Clickhouse"
27 | Iceberg = "Iceberg"
28 | DEFAULT = Iceberg
29 |
30 |
31 | class CommunityEnvironments(enum.Enum):
32 | Staging = "Staging"
33 | Production = "Production"
34 |
35 |
36 | class CommunityConfigurationActions(enum.Enum):
37 | EMAIL_CONFIRM_CODE = "email_confirm_code"
38 |
39 |
40 | class OptimizerModes(enum.Enum):
41 | NORMAL = "normal"
42 | GENETIC = "genetic"
43 |
44 |
45 | class OptimizerConfig(enum.Enum):
46 | OPTIMIZER_ID = "optimizer_id"
47 | OPTIMIZER_IDS = "optimizer_ids"
48 | RANDOMLY_CHOSE_RUNS = "randomly_chose_runs"
49 | DATA_FILES = "data_files"
50 | OPTIMIZER_CONFIG = "optimizer_config"
51 | EXCHANGE_TYPE = "exchange_type"
52 | QUEUE_SIZE = "queue_size"
53 | EMPTY_THE_QUEUE = "empty_the_queue"
54 | START_TIMESTAMP = "start_timestamp"
55 | END_TIMESTAMP = "end_timestamp"
56 | IDLE_CORES = "idle_cores"
57 | NOTIFY_WHEN_COMPLETE = "notify_when_complete"
58 | DB_UPDATE_PERIOD = "db_update_period"
59 | MODE = "mode"
60 | MAX_OPTIMIZER_RUNS = "max_optimizer_runs"
61 | INITIAL_GENERATION_COUNT = "initial_generation_count"
62 | DEFAULT_GENERATIONS_COUNT = "default_generations_count"
63 | DEFAULT_RUN_PER_GENERATION = "default_run_per_generation"
64 | DEFAULT_SCORING_PARAMETERS = "default_scoring_parameters"
65 | DEFAULT_OPTIMIZER_FILTERS = "default_optimizer_filters"
66 | DEFAULT_OPTIMIZER_CONSTRAINTS = "default_optimizer_constraints"
67 | DEFAULT_MUTATION_PERCENT = "default_mutation_percent"
68 | MAX_MUTATION_PROBABILITY_PERCENT = "max_mutation_probability_percent"
69 | MIN_MUTATION_PROBABILITY_PERCENT = "min_mutation_probability_percent"
70 | DEFAULT_MAX_MUTATION_NUMBER_MULTIPLIER = "default_max_mutation_number_multiplier"
71 | DEFAULT_CROSSOVER_PERCENT = "default_crossover_percent"
72 | STAY_WITHIN_BOUNDARIES = "stay_within_boundaries"
73 | TARGET_FITNESS_SCORE = "target_fitness_score"
74 |
75 |
76 | class OctoBotDistribution(enum.Enum):
77 | DEFAULT = "default"
78 | MARKET_MAKING = "market_making"
79 |
--------------------------------------------------------------------------------
/octobot/errors.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | class DisabledError(Exception):
18 | pass
19 |
--------------------------------------------------------------------------------
/octobot/initializer.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot_tentacles_manager.api as tentacles_manager_api
17 | import octobot.constants as constants
18 | import octobot_commons.databases as databases
19 | import octobot_commons.logging as logging
20 | import octobot_commons.errors as commons_errors
21 | import octobot.databases_util as databases_util
22 |
23 |
24 | class Initializer:
25 | """Initializer class:
26 | - Initialize services, constants and tools
27 | """
28 |
29 | def __init__(self, octobot):
30 | self.octobot = octobot
31 |
32 | async def create(self, init_bot_storage):
33 | # initialize tentacle configuration
34 | tentacles_config_path = self.octobot.get_startup_config(constants.CONFIG_KEY, dict_only=False).\
35 | get_tentacles_config_path()
36 | self.octobot.tentacles_setup_config = tentacles_manager_api.get_tentacles_setup_config(tentacles_config_path)
37 |
38 | if init_bot_storage:
39 | try:
40 | # init bot storage
41 | await databases.init_bot_storage(
42 | self.octobot.bot_id,
43 | databases_util.get_run_databases_identifier(
44 | self.octobot.config,
45 | self.octobot.tentacles_setup_config
46 | ),
47 | True
48 | )
49 | except commons_errors.ConfigTradingError as err:
50 | # already logged as error, don't display it twice
51 | logging.get_logger(self.__class__.__name__).warning(f"Error when initializing bot storage: {err}")
52 | except Exception as err:
53 | logging.get_logger(self.__class__.__name__).exception(
54 | err, True, f"Error when initializing bot storage: {err}"
55 | )
56 |
57 | # create OctoBot channel
58 | await self.octobot.global_consumer.initialize()
59 |
--------------------------------------------------------------------------------
/octobot/producers/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.producers import interface_producer
18 | from octobot.producers import exchange_producer
19 | from octobot.producers import evaluator_producer
20 | from octobot.producers import service_feed_producer
21 |
22 | from octobot.producers.interface_producer import (
23 | InterfaceProducer,
24 | )
25 | from octobot.producers.exchange_producer import (
26 | ExchangeProducer,
27 | )
28 | from octobot.producers.evaluator_producer import (
29 | EvaluatorProducer,
30 | )
31 | from octobot.producers.service_feed_producer import (
32 | ServiceFeedProducer,
33 | )
34 |
35 | __all__ = [
36 | "InterfaceProducer",
37 | "ExchangeProducer",
38 | "EvaluatorProducer",
39 | "ServiceFeedProducer",
40 | ]
41 |
--------------------------------------------------------------------------------
/octobot/producers/evaluator_producer.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot_commons.enums as common_enums
17 |
18 | import octobot_backtesting.api as backtesting_api
19 |
20 | import octobot_evaluators.api as evaluator_api
21 | import octobot_evaluators.octobot_channel_consumer as evaluator_channel_consumer
22 |
23 | import octobot.channels as octobot_channel
24 | import octobot.logger as logger
25 |
26 |
27 | class EvaluatorProducer(octobot_channel.OctoBotChannelProducer):
28 | """EvaluatorFactory class:
29 | - Create evaluators
30 | """
31 |
32 | def __init__(self, channel, octobot):
33 | super().__init__(channel)
34 | self.octobot = octobot
35 | self.tentacles_setup_config = self.octobot.tentacles_setup_config
36 |
37 | self.matrix_id = None
38 |
39 | async def start(self):
40 | await evaluator_api.initialize_evaluators(self.octobot.config, self.tentacles_setup_config)
41 | self.matrix_id = evaluator_api.create_matrix()
42 | await evaluator_api.create_evaluator_channels(
43 | self.matrix_id, is_backtesting=backtesting_api.is_backtesting_enabled(self.octobot.config)
44 | )
45 | await logger.init_evaluator_chan_logger(self.matrix_id)
46 |
47 | async def create_evaluators(self, exchange_configuration):
48 | await self.send(bot_id=self.octobot.bot_id,
49 | subject=common_enums.OctoBotChannelSubjects.CREATION.value,
50 | action=evaluator_channel_consumer.OctoBotChannelEvaluatorActions.EVALUATOR.value,
51 | data={
52 | evaluator_channel_consumer.OctoBotChannelEvaluatorDataKeys.TENTACLES_SETUP_CONFIG.value:
53 | self.octobot.tentacles_setup_config,
54 | evaluator_channel_consumer.OctoBotChannelEvaluatorDataKeys.MATRIX_ID.value:
55 | self.octobot.evaluator_producer.matrix_id,
56 | evaluator_channel_consumer.OctoBotChannelEvaluatorDataKeys.EXCHANGE_CONFIGURATION.value:
57 | exchange_configuration
58 | })
59 |
60 | async def stop(self):
61 | self.logger.debug("Stopping ...")
62 | await evaluator_api.stop_all_evaluator_channels(self.matrix_id)
63 | self.logger.debug("Stopped")
64 |
--------------------------------------------------------------------------------
/octobot/producers/exchange_producer.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import asyncio
17 |
18 | import octobot_commons.enums as common_enums
19 | import octobot_commons.constants as common_constants
20 |
21 | import octobot_trading.api as trading_api
22 | import octobot_trading.octobot_channel_consumer as trading_channel_consumer
23 |
24 | import octobot.channels as octobot_channel
25 |
26 |
27 | class ExchangeProducer(octobot_channel.OctoBotChannelProducer):
28 | def __init__(self, channel, octobot, backtesting, ignore_config=False):
29 | super().__init__(channel)
30 | self.octobot = octobot
31 | self.ignore_config = ignore_config
32 |
33 | self.backtesting = backtesting
34 | self.exchange_manager_ids = []
35 |
36 | self.to_create_exchanges_count = 0
37 | self.created_all_exchanges = asyncio.Event()
38 |
39 | async def start(self):
40 | self.to_create_exchanges_count = 0
41 | self.created_all_exchanges.clear()
42 | for exchange_name in trading_api.get_enabled_exchanges_names(self.octobot.config):
43 | await self.create_exchange(exchange_name, self.backtesting)
44 | self.to_create_exchanges_count += 1
45 |
46 | def register_created_exchange_id(self, exchange_id):
47 | self.exchange_manager_ids.append(exchange_id)
48 | if len(self.exchange_manager_ids) == self.to_create_exchanges_count:
49 | self.created_all_exchanges.set()
50 | self.logger.debug(f"Exchange(s) created")
51 |
52 | async def stop(self):
53 | self.logger.debug("Stopping ...")
54 | for exchange_manager in trading_api.get_exchange_managers_from_exchange_ids(self.exchange_manager_ids):
55 | await trading_api.stop_exchange(exchange_manager)
56 | self.logger.debug("Stopped")
57 |
58 | async def create_exchange(self, exchange_name, backtesting):
59 | await self.send(bot_id=self.octobot.bot_id,
60 | subject=common_enums.OctoBotChannelSubjects.CREATION.value,
61 | action=trading_channel_consumer.OctoBotChannelTradingActions.EXCHANGE.value,
62 | data={
63 | trading_channel_consumer.OctoBotChannelTradingDataKeys.TENTACLES_SETUP_CONFIG.value:
64 | self.octobot.tentacles_setup_config,
65 | trading_channel_consumer.OctoBotChannelTradingDataKeys.MATRIX_ID.value:
66 | self.octobot.evaluator_producer.matrix_id,
67 | trading_channel_consumer.OctoBotChannelTradingDataKeys.BACKTESTING.value: backtesting,
68 | trading_channel_consumer.OctoBotChannelTradingDataKeys.EXCHANGE_CONFIG.value:
69 | self.octobot.config,
70 | trading_channel_consumer.OctoBotChannelTradingDataKeys.EXCHANGE_NAME.value: exchange_name,
71 | })
72 |
--------------------------------------------------------------------------------
/octobot/storage/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | from octobot.storage import trading_metadata
17 | from octobot.storage import db_databases_pruning
18 |
19 | from octobot.storage.trading_metadata import (
20 | clear_run_metadata,
21 | store_run_metadata,
22 | store_backtesting_run_metadata,
23 | )
24 | from octobot.storage.db_databases_pruning import (
25 | enforce_total_databases_max_size
26 | )
27 |
28 |
29 | __all__ = [
30 | "clear_run_metadata",
31 | "store_run_metadata",
32 | "store_backtesting_run_metadata",
33 | "enforce_total_databases_max_size",
34 | ]
35 |
--------------------------------------------------------------------------------
/octobot/storage/db_databases_pruning.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import octobot_commons.databases as databases
17 | import octobot.constants as constants
18 |
19 |
20 | async def enforce_total_databases_max_size():
21 | if constants.ENABLE_RUN_DATABASE_LIMIT:
22 | run_databases_identifier = databases.RunDatabasesProvider.instance().get_any_run_databases_identifier()
23 | pruner = databases.run_databases_pruner_factory(
24 | run_databases_identifier,
25 | constants.MAX_TOTAL_RUN_DATABASES_SIZE,
26 | )
27 | await pruner.explore()
28 | await pruner.prune_oldest_run_databases()
29 |
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.strategy_optimizer import test_suite_result
18 | from octobot.strategy_optimizer import strategy_optimizer
19 | from octobot.strategy_optimizer import strategy_design_optimizer
20 | from octobot.strategy_optimizer import strategy_test_suite
21 |
22 | from octobot.strategy_optimizer.test_suite_result import (
23 | TestSuiteResult,
24 | TestSuiteResultSummary,
25 | )
26 | from octobot.strategy_optimizer.strategy_optimizer import (
27 | StrategyOptimizer,
28 | )
29 | from octobot.strategy_optimizer.fitness_parameter import (
30 | FitnessParameter,
31 | )
32 | from octobot.strategy_optimizer.optimizer_filter import (
33 | OptimizerFilter,
34 | )
35 | from octobot.strategy_optimizer.optimizer_settings import (
36 | OptimizerSettings,
37 | )
38 | from octobot.strategy_optimizer.scored_run_result import (
39 | ScoredRunResult,
40 | )
41 | from octobot.strategy_optimizer.optimizer_constraint import (
42 | OptimizerConstraint,
43 | )
44 | from octobot.strategy_optimizer.strategy_design_optimizer import (
45 | StrategyDesignOptimizer,
46 | )
47 | from octobot.strategy_optimizer.strategy_test_suite import (
48 | StrategyTestSuite,
49 | )
50 | from octobot.strategy_optimizer.strategy_design_optimizer_factory import (
51 | create_most_advanced_strategy_design_optimizer,
52 | )
53 |
54 | __all__ = [
55 | "TestSuiteResult",
56 | "TestSuiteResultSummary",
57 | "StrategyOptimizer",
58 | "FitnessParameter",
59 | "OptimizerFilter",
60 | "OptimizerSettings",
61 | "ScoredRunResult",
62 | "OptimizerConstraint",
63 | "StrategyDesignOptimizer",
64 | "StrategyTestSuite",
65 | "create_most_advanced_strategy_design_optimizer",
66 | ]
67 |
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/fitness_parameter.py:
--------------------------------------------------------------------------------
1 | # Drakkar-Software OctoBot
2 | # Copyright (c) Drakkar-Software, All rights reserved.
3 | #
4 | # This library is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU Lesser General Public
6 | # License as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # This library is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # Lesser General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Lesser General Public
15 | # License along with this library.
16 |
17 |
18 | class FitnessParameter:
19 | NAME_KEY = "name"
20 | WEIGHT_KEY = "weight"
21 | IS_RATIO_FROM_MAX_KEY = "is_ratio_from_max"
22 |
23 | def __init__(self, name, weight, is_ratio_from_max):
24 | self.name = name
25 | self.weight = weight
26 | self.is_ratio_from_max = is_ratio_from_max
27 | self.max_ratio_value = None
28 | self.min_ratio_value = None
29 |
30 | def get_normalized_value(self, raw_value):
31 | if self.is_ratio_from_max:
32 | # use ratio if relevant
33 | return self._get_value_from_ratio(raw_value) * self.weight
34 | return raw_value * self._get_parameter_normalizer() * self.weight
35 |
36 | def _get_value_from_ratio(self, raw_value):
37 | return (
38 | raw_value * self._get_parameter_normalizer() if self.max_ratio_value is None
39 | else (raw_value - self.min_ratio_value) / (self.max_ratio_value - self.min_ratio_value)
40 | )
41 |
42 | def _get_parameter_normalizer(self):
43 | return 0.01 if "%" in self.name else 1
44 |
45 | def update_ratio(self, full_result):
46 | try:
47 | if self.max_ratio_value is None or full_result[self.name] > self.max_ratio_value:
48 | self.max_ratio_value = full_result[self.name]
49 | if self.min_ratio_value is None or full_result[self.name] < self.min_ratio_value:
50 | self.min_ratio_value = full_result[self.name]
51 | except KeyError:
52 | pass
53 |
54 | @classmethod
55 | def from_dict(cls, param_dict):
56 | return cls(
57 | param_dict[cls.NAME_KEY],
58 | param_dict[cls.WEIGHT_KEY],
59 | param_dict[cls.IS_RATIO_FROM_MAX_KEY],
60 | )
61 |
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_constraint.py:
--------------------------------------------------------------------------------
1 | # Drakkar-Software OctoBot
2 | # Copyright (c) Drakkar-Software, All rights reserved.
3 | #
4 | # This library is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU Lesser General Public
6 | # License as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # This library is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # Lesser General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Lesser General Public
15 | # License along with this library.
16 |
17 |
18 | class OptimizerConstraint:
19 | NAME_KEY = "name"
20 | MIN_VAL_KEY = "min_val"
21 | MAX_VAL_KEY = "max_val"
22 | MIN_STEP_KEY = "min_step"
23 | MAX_STEP_KEY = "max_step"
24 | STAY_WITHIN_BOUNDARIES_KEY = "stay_within_boundaries"
25 |
26 | def __init__(self, name, min_val, max_val, min_step, max_step, stay_within_boundaries):
27 | self.name = name
28 | self.min_val = min_val
29 | self.max_val = max_val
30 | self.min_step = min_step
31 | self.max_step = max_step
32 | self.stay_within_boundaries = stay_within_boundaries
33 |
34 | def is_min_max_valid(self, value):
35 | if self.max_val is not None and not value > self.max_val:
36 | return False
37 | if self.min_val is not None and not value < self.min_val:
38 | return False
39 | return True
40 |
41 | @classmethod
42 | def from_dict(cls, param_dict):
43 | return cls(
44 | param_dict.get(cls.NAME_KEY),
45 | param_dict.get(cls.MIN_VAL_KEY),
46 | param_dict.get(cls.MAX_VAL_KEY),
47 | param_dict.get(cls.MIN_STEP_KEY),
48 | param_dict.get(cls.MAX_STEP_KEY),
49 | param_dict.get(cls.STAY_WITHIN_BOUNDARIES_KEY),
50 | )
51 |
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774950.9324272.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774950.9324272.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774962.1269426.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774962.1269426.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774974.669779.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774974.669779.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774982.726014.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774982.726014.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774988.7215023.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774988.7215023.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774995.2311237.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581774995.2311237.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775018.2658834.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775018.2658834.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775026.9255266.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775026.9255266.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775117.1713624.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775117.1713624.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775133.1533682.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775133.1533682.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775139.0332782.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775139.0332782.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775144.480404.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775144.480404.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775149.6372743.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775149.6372743.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775154.2598503.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581775154.2598503.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581776404.9679003.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581776404.9679003.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581776676.5721796.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/AbstractExchangeHistoryCollector_1581776676.5721796.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_data_files/ExchangeHistoryDataCollector_1711122002.311132.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/octobot/strategy_optimizer/optimizer_data_files/ExchangeHistoryDataCollector_1711122002.311132.data
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/optimizer_filter.py:
--------------------------------------------------------------------------------
1 | # Drakkar-Software OctoBot
2 | # Copyright (c) Drakkar-Software, All rights reserved.
3 | #
4 | # This library is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU Lesser General Public
6 | # License as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # This library is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # Lesser General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Lesser General Public
15 | # License along with this library.
16 | import decimal
17 | import octobot_commons.logical_operators as logical_operators
18 |
19 |
20 | class OptimizerFilter:
21 | LEFT_OPERAND_KEY_KEY = "left_operand_key"
22 | RIGHT_OPERAND_KEY_KEY = "right_operand_key"
23 | LEFT_OPERAND_VALUE_KEY = "left_operand_value"
24 | RIGHT_OPERAND_VALUE_KEY = "right_operand_value"
25 | OPERATOR_KEY = "operator"
26 |
27 | def __init__(self, left_operand_key, right_operand_key, left_operand_value, right_operand_value, operator):
28 | self.left_operand_key = left_operand_key
29 | self.right_operand_key = right_operand_key
30 | self.left_operand_value = left_operand_value
31 | self.right_operand_value = right_operand_value
32 | self.operator = operator
33 |
34 | def is_valid(self):
35 | return self.left_operand_value and self.right_operand_value and self.operator
36 |
37 | def load_values(self, values: dict):
38 | succeeded = False
39 | if self.left_operand_key is not None:
40 | try:
41 | self.left_operand_value = values[self.left_operand_key]
42 | succeeded = True
43 | except KeyError:
44 | pass
45 | if self.right_operand_key is not None:
46 | try:
47 | self.right_operand_value = values[self.right_operand_key]
48 | except KeyError:
49 | if not succeeded:
50 | # require at least one value read
51 | raise
52 |
53 | def is_filtered(self):
54 | if not self.is_valid():
55 | return False
56 | try:
57 | left_operand = decimal.Decimal(self.left_operand_value)
58 | except decimal.InvalidOperation:
59 | left_operand = str(self.left_operand_value)
60 | try:
61 | right_operand = decimal.Decimal(self.right_operand_value)
62 | except decimal.InvalidOperation:
63 | right_operand = str(self.right_operand_value)
64 | return logical_operators.evaluate_condition(left_operand, right_operand, self.operator)
65 |
66 | @classmethod
67 | def from_dict(cls, param_dict):
68 | return cls(
69 | param_dict[cls.LEFT_OPERAND_KEY_KEY],
70 | param_dict[cls.RIGHT_OPERAND_KEY_KEY],
71 | param_dict[cls.LEFT_OPERAND_VALUE_KEY],
72 | param_dict[cls.RIGHT_OPERAND_VALUE_KEY],
73 | param_dict[cls.OPERATOR_KEY],
74 | )
75 |
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/scored_run_result.py:
--------------------------------------------------------------------------------
1 | # Drakkar-Software OctoBot
2 | # Copyright (c) Drakkar-Software, All rights reserved.
3 | #
4 | # This library is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU Lesser General Public
6 | # License as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # This library is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # Lesser General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Lesser General Public
15 | # License along with this library.
16 |
17 |
18 | class ScoredRunResult:
19 | def __init__(self, full_result, optimizer_run_data):
20 | self.full_result = full_result
21 | self.optimizer_run_data = optimizer_run_data
22 | self.values = {}
23 | self.score = 0
24 | self.total_weight = 0
25 |
26 | def compute_score(self, relevant_scoring_parameters):
27 | self.score = 0
28 | try:
29 | self.score = sum([
30 | self._compute_score(scoring_parameter)
31 | for scoring_parameter in relevant_scoring_parameters
32 | ]) / self.total_weight
33 | except ZeroDivisionError:
34 | self.score = 0
35 |
36 | def _compute_score(self, fitness_parameter):
37 | try:
38 | self.values[fitness_parameter.name] = self.full_result[fitness_parameter.name]
39 | score = fitness_parameter.get_normalized_value(self.values[fitness_parameter.name])
40 | self.total_weight += fitness_parameter.weight
41 | return score
42 | except KeyError:
43 | return 0
44 |
45 | def __repr__(self):
46 | return f"[{self.__class__.__name__}] score: {self.score}, total_weight: {self.total_weight}"
47 |
48 | def result_str(self):
49 | # todo move constants outside of StrategyDesignOptimizer
50 | import octobot.strategy_optimizer.strategy_design_optimizer as strategy_design_optimizer
51 | user_inputs = {
52 | ui[strategy_design_optimizer.StrategyDesignOptimizer.CONFIG_USER_INPUT]:
53 | ui[strategy_design_optimizer.StrategyDesignOptimizer.CONFIG_VALUE]
54 | for ui in self.optimizer_run_data
55 | }
56 | return f"fitness score: {self.score} {self.values} from {user_inputs}"
57 |
--------------------------------------------------------------------------------
/octobot/strategy_optimizer/strategy_design_optimizer_factory.py:
--------------------------------------------------------------------------------
1 | # Drakkar-Software OctoBot
2 | # Copyright (c) Drakkar-Software, All rights reserved.
3 | #
4 | # This library is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU Lesser General Public
6 | # License as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # This library is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # Lesser General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU Lesser General Public
15 | # License along with this library.
16 | import octobot_commons.tentacles_management.class_inspector as class_inspector
17 | import octobot.strategy_optimizer.strategy_design_optimizer as strategy_design_optimizer
18 |
19 |
20 | def create_most_advanced_strategy_design_optimizer(
21 | trading_mode, config, tentacles_setup_config, optimizer_settings=None
22 | ):
23 | advanced_class = strategy_design_optimizer.StrategyDesignOptimizer
24 | optimizer_classes = [
25 | optimizer_class
26 | for optimizer_class in class_inspector.get_all_classes_from_parent(advanced_class)
27 | if optimizer_class.ALLOWED_IN_FACTORY
28 | ]
29 | if optimizer_classes:
30 | # the last one of the list is the most advanced one
31 | advanced_class = optimizer_classes[-1]
32 | return advanced_class(trading_mode, config, tentacles_setup_config, optimizer_settings=optimizer_settings)
33 |
--------------------------------------------------------------------------------
/octobot/updater/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | from octobot.updater import updater_factory
18 | from octobot.updater.updater_factory import (
19 | create_updater,
20 | )
21 |
22 | from octobot.updater import updater
23 | from octobot.updater.updater import (
24 | Updater,
25 | )
26 |
27 | from octobot.updater import binary_updater
28 | from octobot.updater.binary_updater import (
29 | BinaryUpdater,
30 | )
31 | from octobot.updater import python_updater
32 | from octobot.updater.python_updater import (
33 | PythonUpdater,
34 | )
35 |
36 | __all__ = [
37 | "Updater",
38 | "create_updater",
39 | "BinaryUpdater",
40 | "PythonUpdater",
41 | ]
42 |
--------------------------------------------------------------------------------
/octobot/updater/updater.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import packaging.version as packaging_version
17 |
18 | import octobot.constants as constants
19 | import octobot.configuration_manager as configuration_manager
20 | import octobot.commands as commands
21 | import octobot_commons.logging as logging
22 | import octobot_commons.authentication as authentication
23 |
24 |
25 | class Updater:
26 | def __init__(self):
27 | self.logger = logging.get_logger(self.__class__.__name__)
28 |
29 | async def should_be_updated(self):
30 | """
31 | :return: True if the updater version is greater than current bot version
32 | """
33 | try:
34 | latest_version = await self.get_latest_version()
35 | if latest_version is None:
36 | return False
37 | return packaging_version.parse(latest_version) > packaging_version.parse(constants.VERSION)
38 | except TypeError as e:
39 | self.logger.debug(f"Error when comparing latest with current OctoBot version: {e}")
40 |
41 | async def get_latest_version(self):
42 | raise NotImplementedError("get_latest_version is not implemented")
43 |
44 | async def update_impl(self) -> bool:
45 | raise NotImplementedError("update_impl is not implemented")
46 |
47 | async def update_tentacles(self):
48 | bot_version = await self.get_latest_version()
49 | authenticator = authentication.Authenticator.instance()
50 | additional_tentacles_package_urls = authenticator.get_saved_package_urls()
51 | await commands.install_all_tentacles(
52 | tentacles_url=configuration_manager.get_default_tentacles_url(version=bot_version),
53 | additional_tentacles_package_urls=additional_tentacles_package_urls,
54 | bot_version=bot_version
55 | )
56 |
57 | async def post_update(self):
58 | await self.update_tentacles()
59 | commands.restart_bot()
60 |
61 | async def update(self):
62 | """
63 | Call updater update_impl and updates tentacles on update success
64 | """
65 | if await self.update_impl():
66 | await self.post_update()
67 |
--------------------------------------------------------------------------------
/octobot/updater/updater_factory.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | import octobot_commons.os_util as os_util
18 | import octobot_commons.enums as commons_enums
19 |
20 | import octobot.updater.binary_updater as binary_updater
21 | import octobot.updater.python_updater as python_updater
22 |
23 |
24 | def create_updater():
25 | bot_type = os_util.get_octobot_type()
26 |
27 | if bot_type == commons_enums.OctoBotTypes.DOCKER.value:
28 | return None
29 | if bot_type == commons_enums.OctoBotTypes.BINARY.value:
30 | return binary_updater.BinaryUpdater()
31 | if bot_type == commons_enums.OctoBotTypes.PYTHON.value:
32 | return python_updater.PythonUpdater()
33 | return None
34 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # Drakkar-Software requirements
2 | OctoBot-Commons==1.9.83
3 | OctoBot-Trading==2.4.228
4 | OctoBot-Evaluators==1.9.7
5 | OctoBot-Tentacles-Manager==2.9.17
6 | OctoBot-Services==1.6.26
7 | OctoBot-Backtesting==1.9.7
8 | Async-Channel==2.2.1
9 | trading-backend==1.2.42
10 |
11 | ## Others
12 | colorlog==6.8.0
13 | requests==2.32.5
14 | urllib3 # required by requests, used in imports: make sure it's always available
15 | packaging==25.0
16 | python-dotenv==1.1.1
17 | setuptools==79.0.1 # warning: setuptools>=80 breaks easy_install, need to find an alternative not to break installs
18 | # see https://community.palantir.com/t/important-update-on-setuptools-pinning-the-version-below-80-0-0/3872
19 |
20 | # Community
21 | websockets==15.0.1 # used by supabase, a recent version is required, see https://github.com/supabase/realtime-py/blob/main/pyproject.toml
22 | gmqtt==0.7.0
23 | pgpy==0.6.0
24 | clickhouse-connect==0.8.18
25 | pyiceberg==0.10.0
26 | pydantic<2.12 # required for pyiceberg 0.10.0 https://github.com/apache/iceberg-python/issues/2590
27 | pyarrow==21.0.0
28 |
29 | # Error tracking
30 | sentry-sdk==2.35.0 # always make sure sentry_aiohttp_transport.py keep working
31 |
32 | # Supabase ensure supabase_backend_tests keep passing when updating any of those
33 | supabase==2.18.1 # Supabase client
34 | supabase_auth # Supabase authenticated API (required by supabase and enforced to allow direct import)
35 | postgrest # Supabase posgres calls (required by supabase and enforced to allow direct import)
36 |
37 | # async http requests
38 | aiohttp==3.12.15
39 | # updating to aiodns==3.2.0 is incompatible (and failing CI)
40 | # raises RuntimeError: aiodns needs a SelectorEventLoop on Windows. See more: https://github.com/saghul/aiodns/issues/86
41 | aiodns==3.1.1 # used by aiohttp
42 |
43 | # used by ccxt for protobuf "websockets" such as mexc
44 | # lock protobuf to avoid using .rc versions
45 | protobuf==5.29.5
46 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [aliases]
2 | release = egg_info -Db ''
3 |
4 | [bdist_wheel]
5 | universal = 1
6 |
7 | [metadata]
8 | license_file = LICENSE
9 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | from setuptools import find_packages
17 | from setuptools import setup
18 | from octobot import PROJECT_NAME, AUTHOR, VERSION
19 |
20 | PACKAGES = find_packages(exclude=["tentacles*", "tests", ])
21 |
22 | # long description from README file
23 | with open('README.md', encoding='utf-8') as f:
24 | DESCRIPTION = f.read()
25 |
26 |
27 | def ignore_git_requirements(requirements):
28 | return [requirement for requirement in requirements if "git+" not in requirement]
29 |
30 |
31 | REQUIRED = ignore_git_requirements(open('requirements.txt').readlines())
32 | REQUIRES_PYTHON = '>=3.10'
33 |
34 | setup(
35 | name=PROJECT_NAME.lower().replace("-", "_"),
36 | version=VERSION,
37 | url='https://github.com/Drakkar-Software/OctoBot',
38 | license='GPL-3.0',
39 | author=AUTHOR,
40 | author_email='contact@drakkar.software',
41 | description='Cryptocurrencies alert / trading bot',
42 | py_modules=['start'],
43 | packages=PACKAGES,
44 | package_data={
45 | "": ["config/*", "strategy_optimizer/optimizer_data_files/*"],
46 | },
47 | long_description=DESCRIPTION,
48 | long_description_content_type='text/markdown',
49 | tests_require=["pytest"],
50 | test_suite="tests",
51 | zip_safe=False,
52 | install_requires=REQUIRED,
53 | python_requires=REQUIRES_PYTHON,
54 | entry_points={
55 | 'console_scripts': [
56 | PROJECT_NAME + ' = octobot.cli:main'
57 | ]
58 | },
59 | classifiers=[
60 | 'Development Status :: 4 - Beta',
61 | 'Operating System :: OS Independent',
62 | 'Operating System :: MacOS :: MacOS X',
63 | 'Operating System :: Microsoft :: Windows',
64 | 'Operating System :: POSIX',
65 | 'Programming Language :: Python :: 3.10',
66 | ],
67 | )
68 |
--------------------------------------------------------------------------------
/start.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import sys
17 |
18 | from octobot.cli import main
19 |
20 | if __name__ == '__main__':
21 | main(sys.argv[1:])
22 |
--------------------------------------------------------------------------------
/tests/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG OCTOBOT_IMAGE
2 | FROM $OCTOBOT_IMAGE
3 |
4 | # Make sure we use the virtualenv:
5 | ENV PATH="/opt/venv/bin:$PATH"
6 |
7 | ENV CYTHON_IGNORE=true
8 |
9 | COPY dev_requirements.txt .
10 | COPY tests tests
11 | RUN pip freeze && pip install --prefer-binary -r dev_requirements.txt
12 |
13 | ENTRYPOINT ["./tests/docker-entrypoint.sh"]
14 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # check python libs
4 | python -m pip freeze
5 |
6 | # install tentacles
7 | if ./OctoBot tentacles --install -a ; then
8 | echo "Tentacles successfully installed"
9 | else
10 | unset TENTACLES_REPOSITORY
11 | export TENTACLES_URL_TAG=latest
12 | ./OctoBot tentacles --install -a
13 | fi
14 |
15 | # run tests
16 | pytest -rw --ignore=tentacles/Trading/Exchange tests tentacles
17 |
--------------------------------------------------------------------------------
/tests/functional_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/functional_tests/automations/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import mock
17 |
18 | import octobot.automation
19 | import octobot_commons.configuration as configuration
20 |
21 |
22 | class TestTriggerEvent(octobot.automation.AbstractTriggerEvent):
23 | def __init__(self):
24 | super().__init__()
25 | self.get_next_event_mock = mock.AsyncMock()
26 | self.trigger_only_once = True
27 |
28 | async def _get_next_event(self):
29 | # trigger instantly
30 | await self.get_next_event_mock()
31 |
32 | @staticmethod
33 | def get_description() -> str:
34 | return ""
35 |
36 | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
37 | return {}
38 |
39 | def apply_config(self, config):
40 | return
41 |
42 |
43 | class TestCondition(octobot.automation.AbstractCondition):
44 | def __init__(self):
45 | super().__init__()
46 | self.evaluate_mock = mock.AsyncMock()
47 |
48 | async def evaluate(self) -> bool:
49 | await self.evaluate_mock()
50 | return True
51 |
52 | @staticmethod
53 | def get_description() -> str:
54 | return ""
55 |
56 | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
57 | return {}
58 |
59 | def apply_config(self, config):
60 | return
61 |
62 |
63 | class TestAction(octobot.automation.AbstractAction):
64 | def __init__(self):
65 | super().__init__()
66 | self.process_mock = mock.AsyncMock()
67 |
68 | async def process(self):
69 | # trigger instantly
70 | await self.process_mock()
71 |
72 | @staticmethod
73 | def get_description() -> str:
74 | return ""
75 |
76 | def get_user_inputs(self, UI: configuration.UserInputFactory, inputs: dict, step_name: str) -> dict:
77 | return {}
78 |
79 | def apply_config(self, config):
80 | return
81 |
--------------------------------------------------------------------------------
/tests/functional_tests/backtesting_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/functional_tests/evaluators_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/functional_tests/launch_test/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/functional_tests/launch_test/launch_test.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import asyncio
17 | import pytest
18 |
19 | from octobot_commons.logging.logging_util import get_logger
20 | from octobot_trading.api.exchange import cancel_ccxt_throttle_task
21 | from tentacles.Services.Interfaces.web_interface import WebInterface
22 | from octobot_commons.tests.test_config import load_test_config
23 | from octobot.commands import start_bot
24 | from octobot.logger import init_logger
25 | from octobot.octobot import OctoBot
26 | import octobot.community as community
27 |
28 | # All test coroutines will be treated as marked.
29 | pytestmark = pytest.mark.asyncio
30 |
31 |
32 | @pytest.mark.timeout(12)
33 | async def test_run_bot():
34 | # avoid web interface in this test
35 | WebInterface.enabled = False
36 | community.IdentifiersProvider.use_production()
37 | bot = OctoBot(load_test_config(dict_only=False), ignore_config=True)
38 | bot.task_manager.init_async_loop()
39 | await start_bot(bot, init_logger())
40 | await asyncio.sleep(10)
41 | await stop_bot(bot)
42 |
43 |
44 | async def stop_bot(bot):
45 | # force all logger enable to display any error
46 | stop_logger = get_logger("StopBotLogger")
47 | import logging
48 | for logger in logging.Logger.manager.loggerDict.values():
49 | logger.disabled = False
50 |
51 | stop_logger.info("Stopping tasks...")
52 | await bot.stop()
53 |
54 | if bot.task_manager.tools_task_group:
55 | bot.task_manager.tools_task_group.cancel()
56 |
57 | # close community session
58 | if bot.community_handler:
59 | await bot.community_handler.stop_task()
60 |
61 | cancel_ccxt_throttle_task()
62 |
63 | stop_logger.info("Tasks stopped.")
64 |
--------------------------------------------------------------------------------
/tests/functional_tests/strategy_evaluators_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/static/AbstractExchangeHistoryCollector_1586017993.616272.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/tests/static/AbstractExchangeHistoryCollector_1586017993.616272.data
--------------------------------------------------------------------------------
/tests/static/ExchangeHistoryDataCollector_1587769859.9278197.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/tests/static/ExchangeHistoryDataCollector_1587769859.9278197.data
--------------------------------------------------------------------------------
/tests/static/ExchangeHistoryDataCollector_1588110698.1060486.data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Drakkar-Software/OctoBot/69eb3bb956ff64b4fdda2ad23830d493bb4be23b/tests/static/ExchangeHistoryDataCollector_1588110698.1060486.data
--------------------------------------------------------------------------------
/tests/static/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "time_frame": [
3 | "1h",
4 | "4h",
5 | "1d"
6 | ],
7 | "exchanges": {
8 | "binanceus": {
9 | "api-key": "",
10 | "api-secret": "",
11 | "web-socket": false
12 | }
13 | },
14 | "services": {},
15 | "notification": {
16 | "global-info": true,
17 | "notification-type": [],
18 | "price-alerts": true,
19 | "trades": true
20 | },
21 | "backtesting": {
22 | "enabled": false,
23 | "files": [
24 | "tests/static/binance_BTC_USDT_20180428_121156.data",
25 | "tests/static/binance_ETH_USDT_20180716_131148.data",
26 | "tests/static/binance_ICX_BTC_20180716_131148.data",
27 | "tests/static/binance_NEO_BTC_20180716_131148.data",
28 | "tests/static/binance_VEN_BTC_20180716_131148.data",
29 | "tests/static/binance_XRB_BTC_20180716_131148.data",
30 | "tests/static/binance_ONT_BTC_20180722_230900.data",
31 | "tests/static/binance_XLM_BTC_20180722_234305.data",
32 | "tests/static/binance_POWR_BTC_20180722_234855.data",
33 | "tests/static/binance_ADA_BTC_20180722_223335.data",
34 | "tests/static/bittrex_ETC_BTC_20180726_210341.data",
35 | "tests/static/bittrex_WAX_BTC_20180726_205032.data",
36 | "tests/static/bittrex_XRP_BTC_20180726_210927.data",
37 | "tests/static/bittrex_XVG_BTC_20180726_211225.data"
38 | ]
39 | }
40 | }
--------------------------------------------------------------------------------
/tests/static/default_tentacles_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "tentacle_activation": {
3 | "Backtesting": {
4 | "ExchangeHistoryDataCollector": true,
5 | "ExchangeLiveDataCollector": true,
6 | "GenericExchangeDataImporter": true,
7 | "LegacyDataConverter": true
8 | },
9 | "Evaluator": {
10 | "ADXMomentumEvaluator": false,
11 | "BBMomentumEvaluator": false,
12 | "DipAnalyserStrategyEvaluator": false,
13 | "DoubleMovingAverageTrendEvaluator": true,
14 | "EMADivergenceTrendEvaluator": false,
15 | "GoogleTrendsEvaluator": false,
16 | "InstantFluctuationsEvaluator": false,
17 | "InstantMAEvaluator": false,
18 | "KlingerOscillatorMomentumEvaluator": false,
19 | "KlingerOscillatorReversalConfirmationMomentumEvaluator": false,
20 | "MACDMomentumEvaluator": false,
21 | "MoveSignalsStrategyEvaluator": false,
22 | "OverallStateAnalyser": false,
23 | "PatternAnalyser": false,
24 | "RSIMomentumEvaluator": true,
25 | "RSIWeightMomentumEvaluator": false,
26 | "RedditForumEvaluator": false,
27 | "SimpleStrategyEvaluator": true,
28 | "StatisticAnalysis": false,
29 | "StochasticRSIVolatilityEvaluator": false,
30 | "TechnicalAnalysisStrategyEvaluator": false,
31 | "TelegramSignalEvaluator": false,
32 | "TextAnalysis": false,
33 | "TrendAnalysis": false,
34 | "TwitterNewsEvaluator": false
35 | },
36 | "Services": {
37 | "GoogleService": true,
38 | "GoogleServiceFeed": true,
39 | "RedditService": true,
40 | "RedditServiceFeed": true,
41 | "TelegramBotInterface": true,
42 | "TelegramNotifier": true,
43 | "TelegramService": true,
44 | "TelegramServiceFeed": true,
45 | "TradingViewService": true,
46 | "TradingViewServiceFeed": true,
47 | "TwitterNotifier": true,
48 | "TwitterService": true,
49 | "TwitterServiceFeed": true,
50 | "WebHookService": true,
51 | "WebInterface": true,
52 | "WebNotifier": true,
53 | "WebService": true
54 | },
55 | "Trading": {
56 | "ArbitrageTradingMode": false,
57 | "Binance": true,
58 | "BinanceUS": true,
59 | "AscendEx": true,
60 | "CoinbasePro": true,
61 | "DailyTradingMode": true,
62 | "DefaultCCXTSpotExchange": true,
63 | "DipAnalyserTradingMode": false,
64 | "Kraken": true,
65 | "Kucoin": true,
66 | "SignalTradingMode": false,
67 | "StaggeredOrdersTradingMode": false,
68 | "TradingViewSignalsTradingMode": false
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/static/profile.json:
--------------------------------------------------------------------------------
1 | {
2 | "profile": {
3 | "avatar": "default_profile.png",
4 | "description": "OctoBot default profile.",
5 | "id": "default",
6 | "name": "default"
7 | },
8 | "config": {
9 | "crypto-currencies": {
10 | "Bitcoin": {
11 | "pairs": [
12 | "BTC/USDT",
13 | "BTC/EUR"
14 | ]
15 | },
16 | "Neo": {
17 | "pairs": [
18 | "NEO/BTC"
19 | ]
20 | },
21 | "Ethereum": {
22 | "pairs": [
23 | "ETH/USDT"
24 | ]
25 | },
26 | "Icon": {
27 | "pairs": [
28 | "ICX/BTC"
29 | ]
30 | },
31 | "VeChain": {
32 | "pairs": [
33 | "VEN/BTC"
34 | ]
35 | },
36 | "Nano": {
37 | "pairs": [
38 | "XRB/BTC"
39 | ]
40 | },
41 | "Cardano": {
42 | "pairs": [
43 | "ADA/BTC"
44 | ]
45 | },
46 | "Ontology": {
47 | "pairs": [
48 | "ONT/BTC"
49 | ]
50 | },
51 | "Stellar": {
52 | "pairs": [
53 | "XLM/BTC"
54 | ]
55 | },
56 | "Power Ledger": {
57 | "pairs": [
58 | "POWR/BTC"
59 | ]
60 | },
61 | "Ethereum Classic": {
62 | "pairs": [
63 | "ETC/BTC"
64 | ]
65 | },
66 | "WAX": {
67 | "pairs": [
68 | "WAX/BTC"
69 | ]
70 | },
71 | "XRP": {
72 | "pairs": [
73 | "XRP/BTC"
74 | ]
75 | },
76 | "Verge": {
77 | "pairs": [
78 | "XVG/BTC"
79 | ]
80 | }
81 | },
82 | "exchanges": {},
83 | "trading": {
84 | "risk": 1,
85 | "reference-market": "BTC"
86 | },
87 | "trader": {
88 | "enabled": false
89 | },
90 | "trader-simulator": {
91 | "enabled": true,
92 | "fees": {
93 | "maker": 0.1,
94 | "taker": 0.1
95 | },
96 | "starting-portfolio": {
97 | "BTC": 10,
98 | "USD": 1000
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/tests/static/specific_config/ArbitrageTradingMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "exchanges_to_trade_on": [
3 | "kucoin",
4 | "binance"
5 | ],
6 | "minimal_price_delta_percent": 0.0001,
7 | "portfolio_percent_per_trade": 25,
8 | "required_strategies": [],
9 | "stop_loss_delta_percent": 0.1
10 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/DailyTradingMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "close_to_current_price_difference": 0.005,
3 | "default_config": [
4 | "SimpleStrategyEvaluator"
5 | ],
6 | "required_strategies": [
7 | "SimpleStrategyEvaluator",
8 | "TechnicalAnalysisStrategyEvaluator"
9 | ],
10 | "required_strategies_min_count": 1,
11 | "use_maximum_size_orders": false,
12 | "use_prices_close_to_current_price": false,
13 | "use_stop_orders": true
14 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/DipAnalyserStrategyEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "default_config": [
3 | "KlingerOscillatorReversalConfirmationMomentumEvaluator",
4 | "RSIWeightMomentumEvaluator"
5 | ],
6 | "required_evaluators": [
7 | "InstantFluctuationsEvaluator",
8 | "KlingerOscillatorReversalConfirmationMomentumEvaluator",
9 | "RSIWeightMomentumEvaluator"
10 | ],
11 | "required_time_frames": [
12 | "4h"
13 | ]
14 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/DipAnalyserTradingMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "heavy_weight_price_multiplier": 1.1,
3 | "heavy_weight_volume_multiplier": 1,
4 | "light_weight_price_multiplier": 1.04,
5 | "light_weight_volume_multiplier": 0.5,
6 | "medium_weight_price_multiplier": 1.07,
7 | "medium_weight_volume_multiplier": 0.7,
8 | "required_strategies": [
9 | "DipAnalyserStrategyEvaluator"
10 | ],
11 | "sell_orders_count": 3
12 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/EMADivergenceTrendEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "size": 50,
3 | "short": -2,
4 | "long": 2
5 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/GoogleTrendsEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "refresh_rate_seconds" : 86400,
3 | "relevant_history_months" : 3
4 | }
5 |
--------------------------------------------------------------------------------
/tests/static/specific_config/InstantFluctuationsEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "price_difference_threshold_percent": 1,
3 | "volume_difference_threshold_percent": 400,
4 | "time_frame": "1m"
5 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/MoveSignalsStrategyEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "required_time_frames" : ["30m", "1h", "4h"],
3 | "required_evaluators" : ["InstantFluctuationsEvaluator", "KlingerOscillatorMomentumEvaluator", "BBMomentumEvaluator"],
4 | "default_config" : ["KlingerOscillatorMomentumEvaluator", "BBMomentumEvaluator"]
5 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/RedditForumEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "crypto-currencies": [
3 | {
4 | "crypto-currency": "Bitcoin",
5 | "subreddits": [
6 | "Bitcoin"
7 | ]
8 | },
9 | {
10 | "crypto-currency": "Ethereum",
11 | "subreddits": [
12 | "ethereum"
13 | ]
14 | },
15 | {
16 | "crypto-currency": "NEO",
17 | "subreddits": [
18 | "NEO"
19 | ]
20 | },
21 | {
22 | "crypto-currency": "ICON",
23 | "subreddits": [
24 | "icon"
25 | ]
26 | },
27 | {
28 | "crypto-currency": "NANO",
29 | "subreddits": [
30 | "nanocurrency"
31 | ]
32 | },
33 | {
34 | "crypto-currency": "VeChain",
35 | "subreddits": [
36 | "Vechain"
37 | ]
38 | },
39 | {
40 | "crypto-currency": "VeChain Thor",
41 | "subreddits": [
42 | "Vechain"
43 | ]
44 | },
45 | {
46 | "crypto-currency": "Substratum",
47 | "subreddits": [
48 | "SubstratumNetwork"
49 | ]
50 | },
51 | {
52 | "crypto-currency": "Ethos",
53 | "subreddits": [
54 | "ethos_io"
55 | ]
56 | },
57 | {
58 | "crypto-currency": "Ontology",
59 | "subreddits": [
60 | "OntologyNetwork"
61 | ]
62 | },
63 | {
64 | "crypto-currency": "Binance Coin",
65 | "subreddits": []
66 | }
67 | ]
68 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/SignalTradingMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "close_to_current_price_difference": 0.02,
3 | "required_strategies": [
4 | "MoveSignalsStrategyEvaluator"
5 | ],
6 | "use_maximum_size_orders": false,
7 | "use_prices_close_to_current_price": false,
8 | "use_stop_orders": true
9 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/SimpleStrategyEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "background_social_evaluators": [
3 | "RedditForumEvaluator"
4 | ],
5 | "default_config": [
6 | "DoubleMovingAverageTrendEvaluator",
7 | "RSIMomentumEvaluator"
8 | ],
9 | "re_evaluate_TA_when_social_or_realtime_notification": true,
10 | "required_evaluators": [
11 | "*"
12 | ],
13 | "required_time_frames": [
14 | "1h",
15 | "4h",
16 | "1d"
17 | ],
18 | "social_evaluators_notification_timeout": 3600
19 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/StaggeredOrdersTradingMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "pair_settings": [
3 | {
4 | "increment_percent": 3,
5 | "lower_bound": 3000,
6 | "mirror_order_delay": 0,
7 | "mode": "mountain",
8 | "operational_depth": 100,
9 | "pair": "BTC/USDT",
10 | "spread_percent": 6,
11 | "upper_bound": 6000,
12 | "use_existing_orders_only": false
13 | },
14 | {
15 | "increment_percent": 3,
16 | "lower_bound": 3000,
17 | "mirror_order_delay": 0,
18 | "mode": "mountain",
19 | "operational_depth": 100,
20 | "pair": "BTC/USD",
21 | "spread_percent": 6,
22 | "upper_bound": 6000,
23 | "use_existing_orders_only": false
24 | },
25 | {
26 | "increment_percent": 3,
27 | "lower_bound": 0.0003,
28 | "mirror_order_delay": 0,
29 | "mode": "mountain",
30 | "operational_depth": 50,
31 | "pair": "ADA/ETH",
32 | "spread_percent": 6,
33 | "upper_bound": 0.0007,
34 | "use_existing_orders_only": false
35 | },
36 | {
37 | "increment_percent": 0.3,
38 | "lower_bound": 400,
39 | "mirror_order_delay": 0,
40 | "mode": "mountain",
41 | "operational_depth": 50,
42 | "pair": "ETH/USDT",
43 | "spread_percent": 0.7,
44 | "upper_bound": 500,
45 | "use_existing_orders_only": false
46 | },
47 | {
48 | "increment_percent": 1,
49 | "lower_bound": 0.00012386,
50 | "mirror_order_delay": 0,
51 | "mode": "mountain",
52 | "operational_depth": 50,
53 | "pair": "KNC/BTC",
54 | "spread_percent": 2,
55 | "upper_bound": 0.00017386,
56 | "use_existing_orders_only": false
57 | },
58 | {
59 | "increment_percent": 0.75,
60 | "lower_bound": 0.0253,
61 | "mirror_order_delay": 0,
62 | "mode": "neutral",
63 | "operational_depth": 100,
64 | "pair": "ETH/BTC",
65 | "spread_percent": 2.25,
66 | "upper_bound": 0.0409,
67 | "use_existing_orders_only": false
68 | }
69 | ],
70 | "required_strategies": []
71 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/StochasticRSIVolatilityEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "period": 14,
3 | "low_level": 1,
4 | "high_level": 98
5 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/TechnicalAnalysisStrategyEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatible_evaluator_types": [
3 | "TA",
4 | "REAL_TIME"
5 | ],
6 | "default_config": [
7 | "DoubleMovingAverageTrendEvaluator",
8 | "RSIMomentumEvaluator"
9 | ],
10 | "required_evaluators": [
11 | "*"
12 | ],
13 | "required_time_frames": [
14 | "30m", "1h", "2h", "4h", "1d"
15 | ],
16 | "time_frames_to_weight": [
17 | {
18 | "time_frame": "30m",
19 | "weight": 30
20 | },
21 | {
22 | "time_frame": "1h",
23 | "weight": 50
24 | },
25 | {
26 | "time_frame": "2h",
27 | "weight": 50
28 | },
29 | {
30 | "time_frame": "4h",
31 | "weight": 50
32 | },
33 | {
34 | "time_frame": "1d",
35 | "weight": 30
36 | }
37 | ]
38 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/TelegramSignalEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "telegram-channels": [
3 | "test_telegram_signal_strat"
4 | ]
5 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/TradingViewSignalsTradingMode.json:
--------------------------------------------------------------------------------
1 | {
2 | "close_to_current_price_difference": 0.02,
3 | "required_strategies": [],
4 | "use_market_orders": true,
5 | "use_maximum_size_orders": false
6 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/TwitterNewsEvaluator.json:
--------------------------------------------------------------------------------
1 | {
2 | "crypto-currencies": [
3 | {
4 | "accounts": [
5 | "BTCFoundation"
6 | ],
7 | "crypto-currency": "Bitcoin",
8 | "hashtags": []
9 | },
10 | {
11 | "accounts": [
12 | "ethereum",
13 | "VitalikButerin"
14 | ],
15 | "crypto-currency": "Ethereum",
16 | "hashtags": []
17 | },
18 | {
19 | "accounts": [
20 | "NEO_Blockchain",
21 | "NEOnewstoday",
22 | "NEO_council",
23 | "neotogas",
24 | "NEO_DevCon",
25 | "neonexchange",
26 | "dahongfei"
27 | ],
28 | "crypto-currency": "Neo",
29 | "hashtags": []
30 | },
31 | {
32 | "accounts": [
33 | "helloiconworld"
34 | ],
35 | "crypto-currency": "ICON",
36 | "hashtags": []
37 | },
38 | {
39 | "accounts": [
40 | "nanocurrency"
41 | ],
42 | "crypto-currency": "NANO",
43 | "hashtags": []
44 | },
45 | {
46 | "accounts": [
47 | "sunshinelu24",
48 | "VechainThorCom",
49 | "Vechain1"
50 | ],
51 | "crypto-currency": "VeChain",
52 | "hashtags": []
53 | },
54 | {
55 | "accounts": [
56 | "sunshinelu24",
57 | "VechainThorCom",
58 | "Vechain1"
59 | ],
60 | "crypto-currency": "VeChain Thor",
61 | "hashtags": []
62 | },
63 | {
64 | "accounts": [
65 | "SubstratumNet"
66 | ],
67 | "crypto-currency": "Substratum",
68 | "hashtags": []
69 | },
70 | {
71 | "accounts": [
72 | "Ethos_io"
73 | ],
74 | "crypto-currency": "Ethos",
75 | "hashtags": []
76 | },
77 | {
78 | "accounts": [
79 | "OntologyNetwork"
80 | ],
81 | "crypto-currency": "Ontology",
82 | "hashtags": []
83 | },
84 | {
85 | "accounts": [],
86 | "crypto-currency": "Binance Coin",
87 | "hashtags": []
88 | }
89 | ]
90 | }
--------------------------------------------------------------------------------
/tests/static/specific_config/hollaex.json:
--------------------------------------------------------------------------------
1 | {
2 | "rest": "https://api.hollaex.com",
3 | "stop_exchange_tests_rest": "https://www.earncurve.com.au/api"
4 | }
--------------------------------------------------------------------------------
/tests/static/tentacles_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "tentacle_activation": {
3 | "Backtesting": {
4 | "ExchangeHistoryDataCollector": true,
5 | "ExchangeLiveDataCollector": true,
6 | "GenericExchangeDataImporter": true,
7 | "LegacyDataConverter": true
8 | },
9 | "Evaluator": {
10 | "ADXMomentumEvaluator": false,
11 | "BBMomentumEvaluator": false,
12 | "DipAnalyserStrategyEvaluator": false,
13 | "DoubleMovingAverageTrendEvaluator": true,
14 | "EMADivergenceTrendEvaluator": false,
15 | "GoogleTrendsEvaluator": false,
16 | "InstantFluctuationsEvaluator": false,
17 | "InstantMAEvaluator": false,
18 | "KlingerOscillatorMomentumEvaluator": false,
19 | "KlingerOscillatorReversalConfirmationMomentumEvaluator": false,
20 | "MACDMomentumEvaluator": false,
21 | "MoveSignalsStrategyEvaluator": false,
22 | "OverallStateAnalyser": false,
23 | "PatternAnalyser": false,
24 | "RSIMomentumEvaluator": true,
25 | "RSIWeightMomentumEvaluator": false,
26 | "RedditForumEvaluator": false,
27 | "SimpleStrategyEvaluator": true,
28 | "StatisticAnalysis": false,
29 | "StochasticRSIVolatilityEvaluator": false,
30 | "TechnicalAnalysisStrategyEvaluator": false,
31 | "TelegramSignalEvaluator": false,
32 | "TextAnalysis": false,
33 | "TrendAnalysis": false,
34 | "TwitterNewsEvaluator": false
35 | },
36 | "Services": {
37 | "GoogleService": true,
38 | "GoogleServiceFeed": true,
39 | "RedditService": true,
40 | "RedditServiceFeed": true,
41 | "TelegramBotInterface": true,
42 | "TelegramNotifier": true,
43 | "TelegramService": true,
44 | "TelegramServiceFeed": true,
45 | "TradingViewService": true,
46 | "TradingViewServiceFeed": true,
47 | "TwitterNotifier": true,
48 | "TwitterService": true,
49 | "TwitterServiceFeed": true,
50 | "WebHookService": true,
51 | "WebInterface": true,
52 | "WebNotifier": true,
53 | "WebService": true
54 | },
55 | "Trading": {
56 | "ArbitrageTradingMode": false,
57 | "Binance": true,
58 | "BinanceUS": true,
59 | "Bitmax": true,
60 | "CoinbasePro": true,
61 | "DailyTradingMode": true,
62 | "DefaultCCXTSpotExchange": true,
63 | "DipAnalyserTradingMode": false,
64 | "Kraken": true,
65 | "Kucoin": true,
66 | "SignalTradingMode": false,
67 | "StaggeredOrdersTradingMode": false,
68 | "TradingViewSignalsTradingMode": false
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_missing_trader_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": null,
8 | "simulator_initial_portfolio_value": 10,
9 | "real_initial_portfolio_value": 100,
10 | "initial_watched_markets_value": {
11 | "BTC": 1,
12 | "USDT": 0.00025075791580050704,
13 | "XLM": 10,
14 | "ETC": 10,
15 | "XRB": 10,
16 | "VEN": 10,
17 | "ONT": 10,
18 | "XRP": 10,
19 | "NEO": 10,
20 | "ADA": 10,
21 | "EUR": 10,
22 | "POWR": 10,
23 | "WAX": 10,
24 | "ICX": 10,
25 | "XVG": 10,
26 | "ETH": 10
27 | },
28 | "reference_market": "BTC"
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_missing_trader_s_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": null,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "EUR": 10,
25 | "POWR": 10,
26 | "WAX": 10,
27 | "ICX": 10,
28 | "XVG": 10,
29 | "ETH": 10
30 | },
31 | "reference_market": "BTC"
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_ref_invalid_json.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": 10,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "EUR": 10,
25 | "POWR": 10,
26 | "WAX": 10,
27 | "ICX": 10,
28 | "XVG": 10,
29 | "ETH": 10
30 | },
31 | "reference_market": "BTC"
32 | }
33 |
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_ref_missing_exchange.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": 10,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "EUR": 10,
25 | "POWR": 10,
26 | "WAX": 10,
27 | "ICX": 10,
28 | "XVG": 10,
29 | "ETH": 10
30 | },
31 | "reference_market": "BTC"
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_ref_missing_key.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": 10,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "EUR": 10,
25 | "POWR": 10,
26 | "WAX": 10,
27 | "ICX": 10,
28 | "XVG": 10,
29 | "ETH": 10
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_ref_ref_market_changed.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": 10,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "EUR": 10,
25 | "POWR": 10,
26 | "WAX": 10,
27 | "ICX": 10,
28 | "XVG": 10,
29 | "ETH": 10
30 | },
31 | "reference_market": "BTC2"
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ko_ref_symbols_changed.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": 10,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "ETH": 10
25 | },
26 | "reference_market": "BTC"
27 | }
28 |
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ok_log.1:
--------------------------------------------------------------------------------
1 | 2019-03-26 00:00:48 INFO OctoBot Launcher start.py:107 Version : 0.3.2
2 | 2019-03-26 00:00:48 INFO OctoBot Launcher start.py:111 Loading config files...
3 | 2019-03-26 00:00:49 INFO RedditService logging_util.py:54 Successfully initialized using account.
4 | 2019-03-26 00:00:49 INFO TwitterService logging_util.py:54 Successfully initialized and accessible at:
5 | 2019-03-26 00:00:49 INFO WebService logging_util.py:54 Interface successfully initialized and accessible at:
6 | 2019-03-26 00:00:49 DEBUG Notification logging_util.py:50 Twitter notification disabled
7 | 2019-03-26 00:00:49 DEBUG Notification logging_util.py:50 Telegram disabled
8 | 2019-03-26 00:00:49 INFO ExchangeDispatcher[binance] logging_util.py:54 online with REST api and web socket api
9 | 2019-03-26 00:00:49 INFO TraderSimulator[binance] logging_util.py:54 Starting a fresh new trading simulation session using trader simulator initial portfolio in configuration.
10 | 2019-03-26 00:00:49 INFO PortfolioSimulator[ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']] logging_util.py:54 Current Portfolio : {'BTC': {'available': 100, 'total': 100}, 'USD': {'available': 9999, 'total': 9999}}
11 | 2019-03-26 00:00:50 DEBUG OctoBot logging_util.py:50 Using DailyTradingMode trading mode
12 | 2019-03-26 00:00:50 INFO OctoBot logging_util.py:54 Evaluation threads creation...
13 | 2019-03-26 00:00:50 DEBUG TraderSimulator[binance] logging_util.py:50 Enabled on binance
14 | 2019-03-26 00:00:50 INFO RedditDispatcher logging_util.py:54 Starting dispatcher ...
15 | 2019-03-26 00:00:50 INFO RedditDispatcher logging_util.py:54 Nothing to monitor, dispatcher is going to sleep.
16 | 2019-03-26 00:00:50 INFO TwitterDispatcher logging_util.py:54 Starting dispatcher ...
17 | 2019-03-26 00:00:50 INFO TwitterDispatcher logging_util.py:54 Nothing to monitor, dispatcher is going to sleep.
18 | 2019-03-26 00:00:50 INFO OctoBot logging_util.py:54 Evaluation tasks started...
19 | 2019-03-26 00:00:50 DEBUG Evaluator TASK MANAGER - BTC/USDT - binance - TimeFrames.ONE_DAY logging_util.py:50 ** Notified by GlobalPriceUpdater **
20 | 2019-03-26 00:00:50 DEBUG Evaluator TASK MANAGER - BTC/USDT - binance - TimeFrames.ONE_DAY logging_util.py:50 MATRIX : {<EvaluatorMatrixTypes.TA: 'TA'>: {'RSIMomentumEvaluator': {<TimeFrames.ONE_DAY: '1d'>: -0.589521677519556}, 'DoubleMovingAverageTrendEvaluator': {<TimeFrames.ONE_DAY: '1d'>: -0.0025316988136961653}}, <EvaluatorMatrixTypes.SOCIAL: 'SOCIAL'>: {}, <EvaluatorMatrixTypes.REAL_TIME: 'REAL_TIME'>: {}, <EvaluatorMatrixTypes.STRATEGIES: 'STRATEGIES'>: {'SimpleMixedStrategiesEvaluator': -0.2960266881666261}}
21 | 2019-03-26 00:00:50 INFO DailyTradingModeDecider logging_util.py:54 BTC/USDT ** NEW FINAL STATE ** : EvaluatorStates.LONG
22 | 2019-03-26 00:00:51 INFO TraderSimulator[binance] logging_util.py:54 Order creation : BTC/USDT | BUY_LIMIT | Price : 3894.96 | Quantity : 0.608916 | Status : OPEN
23 | 2019-03-26 00:00:51 DEBUG OrdersManagerSimulator[binance] logging_util.py:50 Order added to open orders (total: 1 open order)
24 |
--------------------------------------------------------------------------------
/tests/static/trading_states_tests/ok_ref.json:
--------------------------------------------------------------------------------
1 | {
2 | "ExchangeSimulator['BTC/USDT', 'ETH/USDT', 'ICX/BTC', 'NEO/BTC', 'VEN/BTC', 'XRB/BTC', 'ONT/BTC', 'XLM/BTC', 'POWR/BTC', 'ADA/BTC', 'ETC/BTC', 'WAX/BTC', 'XRP/BTC', 'XVG/BTC']": {
3 | "simulator_initial_portfolio": {
4 | "BTC": 10,
5 | "USD": 1000
6 | },
7 | "real_initial_portfolio": {
8 | "ZYX": 9999,
9 | "DAI": 1
10 | },
11 | "simulator_initial_portfolio_value": 10,
12 | "real_initial_portfolio_value": 100,
13 | "initial_watched_markets_value": {
14 | "BTC": 1,
15 | "USDT": 0.00025075791580050704,
16 | "XLM": 10,
17 | "ETC": 10,
18 | "XRB": 10,
19 | "VEN": 10,
20 | "ONT": 10,
21 | "XRP": 10,
22 | "NEO": 10,
23 | "ADA": 10,
24 | "EUR": 10,
25 | "POWR": 10,
26 | "WAX": 10,
27 | "ICX": 10,
28 | "XVG": 10,
29 | "ETH": 10
30 | },
31 | "reference_market": "BTC"
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/test_utils/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/test_utils/bot_management.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import asyncio
17 |
18 | from octobot_backtesting.errors import MissingTimeFrame
19 | from octobot_commons.logging.logging_util import get_logger
20 | from octobot_commons.tests.test_config import load_test_config
21 |
22 | from octobot.api.backtesting import create_independent_backtesting, \
23 | initialize_and_run_independent_backtesting, stop_independent_backtesting
24 | from octobot.logger import init_logger
25 | from octobot.octobot import OctoBot
26 | from tests.test_utils.config import load_test_tentacles_config
27 |
28 |
29 | async def create_bot() -> OctoBot:
30 | return OctoBot(load_test_config())
31 |
32 |
33 | async def initialize_bot(bot):
34 | await bot.initialize()
35 |
36 |
37 | async def run_independent_backtesting(data_files, timeout=10, use_loggers=True, run_on_common_part_only=True,
38 | enable_storage=False):
39 | independent_backtesting = None
40 | try:
41 | config_to_use = load_test_config()
42 | if use_loggers:
43 | init_logger()
44 | independent_backtesting = create_independent_backtesting(config_to_use,
45 | load_test_tentacles_config(),
46 | data_files,
47 | "",
48 | run_on_common_part_only=run_on_common_part_only,
49 | enable_storage=enable_storage)
50 | await initialize_and_run_independent_backtesting(independent_backtesting, log_errors=False)
51 | await independent_backtesting.join_backtesting_updater(timeout)
52 | return independent_backtesting
53 | except MissingTimeFrame:
54 | # ignore this exception: is due to missing of the only required time frame
55 | return independent_backtesting
56 | except asyncio.TimeoutError as e:
57 | get_logger().exception(e, True, f"Timeout after waiting for backtesting for {timeout} seconds.")
58 | # stop backtesting to prevent zombie tasks
59 | await stop_independent_backtesting(independent_backtesting)
60 | raise
61 | except Exception as e:
62 | get_logger().exception(e, True, str(e))
63 | # stop backtesting to prevent zombie tasks
64 | await stop_independent_backtesting(independent_backtesting)
65 | raise
66 |
--------------------------------------------------------------------------------
/tests/test_utils/config.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | from octobot_tentacles_manager.api.configurator import get_tentacles_setup_config
17 |
18 | TEST_CONFIG_FOLDER = "tests/static"
19 |
20 |
21 | def load_test_tentacles_config():
22 | return get_tentacles_setup_config(config_path=f"{TEST_CONFIG_FOLDER}/default_tentacles_config.json")
23 |
--------------------------------------------------------------------------------
/tests/test_utils/logging.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 |
18 | def activate_tentacles_loading_logger():
19 | import logging
20 | console = logging.StreamHandler()
21 | console.setLevel(logging.DEBUG)
22 | # add the handler to the TentacleLoader logger
23 | logging.getLogger('TentacleLoader').addHandler(console)
24 |
--------------------------------------------------------------------------------
/tests/test_utils/order_util.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
17 | import random
18 | import time
19 |
20 | from config import SIMULATOR_LAST_PRICES_TO_CHECK
21 |
22 |
23 | async def fill_limit_or_stop_order(limit_or_stop_order, min_price, max_price):
24 | last_prices = []
25 | limit_or_stop_order.created_time = time.time()
26 | for i in range(0, SIMULATOR_LAST_PRICES_TO_CHECK):
27 | last_prices.insert(i, {})
28 | last_prices[i]["price"] = random.uniform(min_price, max_price)
29 | last_prices[i]["timestamp"] = time.time()
30 |
31 | limit_or_stop_order.last_prices = last_prices
32 | await limit_or_stop_order.update_order_status()
33 |
34 |
35 | async def fill_market_order(market_order, price):
36 | last_prices = [{
37 | "price": price
38 | }]
39 |
40 | market_order.last_prices = last_prices
41 | await market_order.update_order_status()
42 |
--------------------------------------------------------------------------------
/tests/test_utils/test_exchanges.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import os
17 |
18 | import octobot_trading.exchanges as exchanges
19 |
20 |
21 | def get_test_exchange_manager(config, exchange_name):
22 | exchange_manager = exchanges.ExchangeManager(config, exchange_name)
23 | # customize exchange id to include test name (useful in possible memory issues)
24 | exchange_manager.id = f"{exchange_manager.id}[{os.environ.get('PYTEST_CURRENT_TEST')}]"
25 | return exchange_manager
26 |
--------------------------------------------------------------------------------
/tests/test_utils/trading_modes.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import asyncio
17 |
18 |
19 | def set_ready_to_start(trading_mode_producer):
20 | trading_mode_producer._is_ready_to_trade = asyncio.Event()
21 | trading_mode_producer._is_ready_to_trade.set()
22 |
--------------------------------------------------------------------------------
/tests/unit_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright
2 |
--------------------------------------------------------------------------------
/tests/unit_tests/community/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/unit_tests/community/errors_upload/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/unit_tests/community/test_community_data.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import pytest
17 |
18 | import octobot.community.community_analysis as community_analysis
19 |
20 |
21 | @pytest.mark.asyncio
22 | async def test_get_community_metrics():
23 | metrics = await community_analysis.get_community_metrics()
24 | assert metrics == {}
25 | return # todo migrate with new metrics
26 | assert len(metrics) == 9
27 | # ensure metrics are not empty
28 | assert all(content for content in metrics.values())
29 |
--------------------------------------------------------------------------------
/tests/unit_tests/strategy_optimizer/test_strategy_optimizer.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import os
17 |
18 | import mock
19 | import builtins
20 |
21 | import tests.test_utils.config as test_utils_config
22 | import octobot_commons.tests.test_config as test_config
23 | import tentacles.Evaluator.Strategies as tentacles_strategies
24 | import octobot.strategy_optimizer as strategy_optimizer
25 |
26 |
27 | class StrategyTestSuiteMock(mock.Mock):
28 | def __init__(self, *args, **kwargs):
29 | super(StrategyTestSuiteMock, self).__init__(*args, **kwargs)
30 | self.run_test_suite = mock.AsyncMock()
31 | self.initialize_with_strategy = mock.Mock()
32 |
33 | def get_test_suite_result(self, *_, **__):
34 | strategy_name = tentacles_strategies.SimpleStrategyEvaluator.get_name()
35 | return strategy_optimizer.TestSuiteResult([(1, 0), (0, -1)], [1, 2], 1, ["tf1", "tf2"], [strategy_name], strategy_name)
36 |
37 |
38 | def test_find_optimal_configuration():
39 | with mock.patch.object(strategy_optimizer, "StrategyTestSuite", StrategyTestSuiteMock()) as test_suite_mock, \
40 | mock.patch.object(builtins, "print", mock.Mock()) as print_mock:
41 | strategy_name = tentacles_strategies.SimpleStrategyEvaluator.get_name()
42 | optimizer = strategy_optimizer.StrategyOptimizer(test_config.load_test_config(),
43 | test_utils_config.load_test_tentacles_config(),
44 | strategy_name)
45 | optimizer.find_optimal_configuration()
46 | if os.getenv('CYTHON_IGNORE'):
47 | return
48 | assert optimizer.total_nb_runs == 21
49 | assert test_suite_mock.call_count == optimizer.total_nb_runs
50 | assert print_mock.call_count == optimizer.total_nb_runs * 2
51 | # check each call has been different
52 | # iterate over each second print call to check run config (each strategy optimizer run prints twice)
53 | for call in print_mock.call_args_list[::2]:
54 | assert len([c for c in print_mock.call_args_list if c == call]) == 1
55 |
--------------------------------------------------------------------------------
/tests/unit_tests/strategy_optimizer/test_strategy_test_suite.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import os
17 |
18 | import mock
19 | import builtins
20 | import pytest
21 |
22 | import octobot.strategy_optimizer as strategy_optimizer
23 |
24 | # All test coroutines will be treated as marked.
25 | pytestmark = pytest.mark.asyncio
26 |
27 |
28 | class StrategyTestSuiteMock(strategy_optimizer.StrategyTestSuite, mock.Mock):
29 | def __init__(self, *args, **kwargs):
30 | super(strategy_optimizer.StrategyTestSuite, self).__init__(*args, **kwargs)
31 | super(mock.Mock, self).__init__(*args, **kwargs)
32 | self.logger = mock.Mock()
33 | self.test_slow_downtrend = mock.AsyncMock()
34 | self.test_sharp_downtrend = mock.AsyncMock()
35 | self.test_flat_markets = mock.AsyncMock()
36 | self.test_slow_uptrend = mock.AsyncMock()
37 | self.test_sharp_uptrend = mock.AsyncMock()
38 | # will raise RuntimeError
39 | self.test_up_then_down = mock.AsyncMock(side_effect=RuntimeError())
40 | self.test_up_then_down.__name__ = "test_up_then_down"
41 |
42 |
43 | async def test_run_test_suite():
44 | test_suite = StrategyTestSuiteMock()
45 | tester_mock = mock.Mock()
46 | if os.getenv('CYTHON_IGNORE'):
47 | return
48 | with mock.patch.object(builtins, "print", mock.Mock()) as print_mock:
49 | assert await test_suite.run_test_suite(tester_mock) is False
50 | calls = (test_suite.test_slow_downtrend, test_suite.test_sharp_downtrend, test_suite.test_flat_markets,
51 | test_suite.test_slow_uptrend, test_suite.test_sharp_uptrend, test_suite.test_up_then_down)
52 | for call in calls:
53 | call.assert_called_once()
54 | assert test_suite.current_progress == 100
55 | assert len(test_suite.exceptions) == 1
56 | assert isinstance(test_suite.exceptions[0], RuntimeError)
57 | test_suite.logger.exception.assert_called_once()
58 | # once for each call and one for the beginning and the end
59 | assert print_mock.call_count == len(calls) + 2
60 |
--------------------------------------------------------------------------------
/tests/unit_tests/test_configuration_manager.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import os
17 |
18 | from octobot.configuration_manager import init_config
19 | from octobot_commons.constants import CONFIG_FILE
20 | from octobot_commons.tests.test_config import TEST_CONFIG_FOLDER
21 |
22 |
23 | def get_fake_config_path():
24 | return os.path.join(TEST_CONFIG_FOLDER, f"test_{CONFIG_FILE}")
25 |
26 |
27 | def test_init_config():
28 | config_path = get_fake_config_path()
29 | if os.path.isfile(config_path):
30 | os.remove(config_path)
31 |
32 | init_config(config_file=config_path, from_config_file=os.path.join(TEST_CONFIG_FOLDER, CONFIG_FILE))
33 | assert os.path.isfile(config_path)
34 | os.remove(config_path)
35 |
--------------------------------------------------------------------------------
/tests/unit_tests/trading_modes_tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/unit_tests/updater/__init__.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 |
--------------------------------------------------------------------------------
/tests/unit_tests/updater/test_updater.py:
--------------------------------------------------------------------------------
1 | # This file is part of OctoBot (https://github.com/Drakkar-Software/OctoBot)
2 | # Copyright (c) 2025 Drakkar-Software, All rights reserved.
3 | #
4 | # OctoBot is free software; you can redistribute it and/or
5 | # modify it under the terms of the GNU General Public License
6 | # as published by the Free Software Foundation; either
7 | # version 3.0 of the License, or (at your option) any later version.
8 | #
9 | # OctoBot is distributed in the hope that it will be useful,
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 | # General Public License for more details.
13 | #
14 | # You should have received a copy of the GNU General Public
15 | # License along with OctoBot. If not, see <https://www.gnu.org/licenses/>.
16 | import pytest
17 |
18 | import octobot.api as octobot_api
19 |
20 | # All test coroutines will be treated as marked.
21 | pytestmark = pytest.mark.asyncio
22 |
23 |
24 | async def test_create_updater_from_api():
25 | updater = octobot_api.get_updater()
26 |
27 |
28 | async def test_should_update_on_updater_from_api():
29 | updater = octobot_api.get_updater()
30 | assert not (await updater.should_be_updated())
31 |
--------------------------------------------------------------------------------