Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ PACKAGE_SOURCES = [
"packages/evaluators:octobot_evaluators",
"packages/node:octobot_node",
"packages/flow:octobot_flow",
"packages/copy:octobot_copy",
"packages/services:octobot_services",
"packages/sync:octobot_sync",
"packages/tentacles_manager:octobot_tentacles_manager",
Expand Down
6 changes: 6 additions & 0 deletions packages/async_channel/async_channel/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""
from async_channel.util import channel_creator
from async_channel.util import logging_util
from async_channel.util import synchronization_util

from async_channel.util.channel_creator import (
create_all_subclasses_channel,
Expand All @@ -28,8 +29,13 @@
get_logger,
)

from async_channel.util.synchronization_util import (
trigger_and_bypass_consumers_queue,
)

__all__ = [
"create_all_subclasses_channel",
"create_channel_instance",
"get_logger",
"trigger_and_bypass_consumers_queue",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Drakkar-Software Async-Channel
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import asyncio
import typing

if typing.TYPE_CHECKING:
import async_channel.consumer


async def trigger_and_bypass_consumers_queue(
consumers: list["async_channel.consumer.Consumer"], kwargs: dict
):
await asyncio.gather(*[
consumer.callback(**kwargs)
for consumer in consumers
])
4 changes: 4 additions & 0 deletions packages/commons/octobot_commons/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
get_password_hash,
)
from octobot_commons.configuration.user_inputs import (
USER_INPUT_TYPE_TO_PYTHON_TYPE,
MAX_USER_INPUT_ORDER,
UserInput,
UserInputFactory,
sanitize_user_input_name,
Expand Down Expand Up @@ -85,6 +87,8 @@
"decrypt",
"decrypt_element_if_possible",
"get_password_hash",
"USER_INPUT_TYPE_TO_PYTHON_TYPE",
"MAX_USER_INPUT_ORDER",
"UserInput",
"UserInputFactory",
"sanitize_user_input_name",
Expand Down
17 changes: 16 additions & 1 deletion packages/commons/octobot_commons/configuration/user_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,23 @@
import octobot_commons.dict_util as dict_util


USER_INPUT_TYPE_TO_PYTHON_TYPE = {
enums.UserInputTypes.INT.value: int,
enums.UserInputTypes.FLOAT.value: float,
enums.UserInputTypes.BOOLEAN.value: bool,
enums.UserInputTypes.TEXT.value: str,
enums.UserInputTypes.OBJECT.value: dict,
enums.UserInputTypes.OBJECT_ARRAY.value: list,
enums.UserInputTypes.STRING_ARRAY.value: list,
enums.UserInputTypes.OPTIONS.value: str,
enums.UserInputTypes.MULTIPLE_OPTIONS.value: list,
}


MAX_USER_INPUT_ORDER = 9999


class UserInput:
MAX_ORDER = 9999

def __init__(
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ def _visit_node(self, node: typing.Optional[ast.AST]) -> typing.Union[
)

raise octobot_commons.errors.UnsupportedOperatorError(
f"Unsupported AST node type: {type(node).__name__}"
f"Unsupported AST node type: {type(node).__name__}. Expression: {self._parsed_expression}"
)

def _get_name_from_node(self, node: ast.AST) -> str:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,26 @@ def get_last_execution_result(
]).last_execution_result
return None

def build_re_callable_result(
def create_re_callable_result(
self,
reset_to_id: typing.Optional[str] = None,
waiting_time: typing.Optional[float] = None,
last_execution_time: typing.Optional[float] = None,
**kwargs: typing.Any,
) -> ReCallingOperatorResult:
"""
Builds a re-callable result from the given parameters.
"""
return ReCallingOperatorResult(
reset_to_id=reset_to_id,
last_execution_result={
ReCallingOperatorResultKeys.WAITING_TIME.value: waiting_time,
ReCallingOperatorResultKeys.LAST_EXECUTION_TIME.value: last_execution_time,
**kwargs,
},
)

def create_re_callable_result_dict(
self,
reset_to_id: typing.Optional[str] = None,
waiting_time: typing.Optional[float] = None,
Expand All @@ -104,12 +123,10 @@ def build_re_callable_result(
Builds a dict formatted re-callable result from the given parameters.
"""
return {
ReCallingOperatorResult.__name__: ReCallingOperatorResult(
ReCallingOperatorResult.__name__: self.create_re_callable_result(
reset_to_id=reset_to_id,
last_execution_result={
ReCallingOperatorResultKeys.WAITING_TIME.value: waiting_time,
ReCallingOperatorResultKeys.LAST_EXECUTION_TIME.value: last_execution_time,
**kwargs,
},
waiting_time=waiting_time,
last_execution_time=last_execution_time,
**kwargs,
).to_dict(include_default_values=False)
}
21 changes: 21 additions & 0 deletions packages/commons/octobot_commons/str_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Drakkar-Software OctoBot-Commons
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import re


def camel_to_snake(name: str) -> str:
"""Convert CamelCase to snake_case (e.g. for DSL operator names)."""
return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
3 changes: 3 additions & 0 deletions packages/commons/octobot_commons/symbols/symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ def __eq__(self, other):
def __str__(self):
return self.symbol_str

def __repr__(self):
return str(self)


def _parse_symbol_full(full_symbol_regex, symbol_str):
return re.search(full_symbol_regex, symbol_str).groups()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ def test_get_last_execution_result_with_reset_to_id_format(self):
})
assert result == inner

def test_build_re_callable_result(self):
def test_create_re_callable_result_dict(self):
operator = _TestReCallableOperator()
result = operator.build_re_callable_result(
result = operator.create_re_callable_result_dict(
last_execution_time=1000.0,
waiting_time=5.0,
)
Expand All @@ -163,9 +163,9 @@ def test_build_re_callable_result(self):
re_callable_operator_mixin.ReCallingOperatorResultKeys.WAITING_TIME.value
] == 5.0

def test_build_re_callable_result_with_reset_to_id(self):
def test_create_re_callable_result_dict_with_reset_to_id(self):
operator = _TestReCallableOperator()
result = operator.build_re_callable_result(
result = operator.create_re_callable_result_dict(
reset_to_id="target_123",
last_execution_time=1000.0,
waiting_time=5.0,
Expand All @@ -174,9 +174,9 @@ def test_build_re_callable_result_with_reset_to_id(self):
assert inner["reset_to_id"] == "target_123"
assert "last_execution_result" in inner

def test_build_re_callable_result_with_extra_kwargs(self):
def test_create_re_callable_result_dict_with_extra_kwargs(self):
operator = _TestReCallableOperator()
result = operator.build_re_callable_result(
result = operator.create_re_callable_result_dict(
last_execution_time=1000.0,
waiting_time=5.0,
extra_field=42,
Expand Down
38 changes: 38 additions & 0 deletions packages/commons/tests/test_str_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Drakkar-Software OctoBot-Commons
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import octobot_commons.str_util as str_util


def test_camel_to_snake_empty():
assert str_util.camel_to_snake("") == ""


def test_camel_to_snake_single_letter():
assert str_util.camel_to_snake("A") == "a"


def test_camel_to_snake_trading_mode_style():
assert str_util.camel_to_snake("GridTradingMode") == "grid_trading_mode"
assert str_util.camel_to_snake("IndexTradingMode") == "index_trading_mode"
assert str_util.camel_to_snake("AbstractTradingMode") == "abstract_trading_mode"


def test_camel_to_snake_already_lowercase():
assert str_util.camel_to_snake("already_snake") == "already_snake"


def test_camel_to_snake_single_word_upper():
assert str_util.camel_to_snake("Trading") == "trading"
17 changes: 17 additions & 0 deletions packages/copy/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
python_sources(
name="octobot_copy",
sources=["octobot_copy/**/*.py"],
dependencies=[
"packages/commons:octobot_commons",
"packages/trading:octobot_trading",
"//:tentacles",
],
)

python_tests(
name="tests",
sources=["tests/**/test_*.py"],
dependencies=[
":octobot_copy",
],
)
3 changes: 3 additions & 0 deletions packages/copy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Mini OctoBot

OctoBot automations runner
Empty file.
29 changes: 29 additions & 0 deletions packages/copy/octobot_copy/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Drakkar-Software OctoBot
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import decimal

# Rebalance planner thresholds
ALLOWED_1_TO_1_SWAP_COUNTS = 1
MIN_RATIO_TO_SELL = decimal.Decimal("0.0001") # 1/10000
QUOTE_ASSET_TO_TARGETED_SWAP_RATIO_THRESHOLD = decimal.Decimal("0.1") # 10%

# Index / rebalancing trading config keys (shared by planner, index trading mode, profiles).
CONFIG_INDEX_CONTENT = "index_content"
CONFIG_REBALANCE_TRIGGER_MIN_PERCENT = "rebalance_trigger_min_percent"
CONFIG_REBALANCE_TRIGGER_PROFILES = "rebalance_trigger_profiles"
CONFIG_SELECTED_REBALANCE_TRIGGER_PROFILE = "selected_rebalance_trigger_profile"
CONFIG_REBALANCE_TRIGGER_PROFILE_NAME = "name"
CONFIG_REBALANCE_TRIGGER_PROFILE_MIN_PERCENT = "min_percent"
38 changes: 38 additions & 0 deletions packages/copy/octobot_copy/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Drakkar-Software OctoBot
# Copyright (c) Drakkar-Software, All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library.
import enum


class RebalanceDetails(enum.Enum):
SELL_SOME = "SELL_SOME"
BUY_MORE = "BUY_MORE"
REMOVE = "REMOVE"
ADD = "ADD"
SWAP = "SWAP"
FORCED_REBALANCE = "FORCED_REBALANCE"


class SynchronizationPolicy(enum.Enum):
SELL_REMOVED_INDEX_COINS_ON_RATIO_REBALANCE = "sell_removed_index_coins_on_ratio_rebalance"
SELL_REMOVED_INDEX_COINS_AS_SOON_AS_POSSIBLE = "sell_removed_index_coins_as_soon_as_possible"
SELL_REMOVED_DYNAMIC_INDEX_COINS_AS_SOON_AS_POSSIBLE = "sell_removed_dynamic_index_coins_as_soon_as_possible"


class DistributionKeys(enum.StrEnum):
NAME = "name"
VALUE = "value"
PRICE = "price"

2 changes: 2 additions & 0 deletions packages/copy/octobot_copy/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class OctobotCopyError(Exception):
"""parent class for all octobot copy errors"""
5 changes: 5 additions & 0 deletions packages/copy/octobot_copy/exchange/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from octobot_copy.exchange.exchange_interface import ExchangeInterface

__all__ = [
"ExchangeInterface",
]
20 changes: 20 additions & 0 deletions packages/copy/octobot_copy/exchange/exchange_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import typing

import octobot_commons.symbols

if typing.TYPE_CHECKING:
import octobot_trading.exchanges

class ExchangeInterface:
def __init__(self, exchange_manager: "octobot_trading.exchanges.ExchangeManager"):
self._exchange_manager: "octobot_trading.exchanges.ExchangeManager" = exchange_manager

@property
def exchange_name(self) -> str:
return self._exchange_manager.exchange_name

def get_traded_symbols(self) -> typing.Iterable[octobot_commons.symbols.Symbol]:
return self._exchange_manager.exchange_config.traded_symbols

def get_time(self) -> float:
return self._exchange_manager.exchange.get_exchange_current_time()
Loading
Loading