Skip to content

Inactive orders #1206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft

Inactive orders #1206

wants to merge 9 commits into from

Conversation

GuillaumeDSM
Copy link
Member

No description provided.

@GuillaumeDSM GuillaumeDSM requested a review from Herklos April 25, 2025 15:35
@GuillaumeDSM GuillaumeDSM self-assigned this Apr 25, 2025
Copy link
Member

@Herklos Herklos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great implementation!

Comment on lines 408 to 412
if self.simulate:
self.logger.error(f"Can't update order as inactive on simulated trading.")
return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we check self.enable_inactive_orders instead self.simulate here ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed!

Comment on lines 99 to 102
self.is_active = True # When is_active=False order is not pushed to exchanges
self.active_trigger_price: decimal.Decimal = None # price threshold from which the order becomes active
# when True, order becomes active when current price >= active_trigger_price
self.active_trigger_above: bool = None
self._active_trigger_event: asyncio.Event = None # will be set when the price is hit
# waiter that will call on_active_trigger() when active_trigger_event is set
self._active_trigger_task: asyncio.Task = None
# True when a transition between active and inactive is being made
self.is_in_active_inactive_transition = False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should create an ActiveState or something like this to manage all attributes and functions related to order activeness. I think it will be easier to abstract the activeness logic inside a dedicated class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in order.active_trigger using the new PriceTrigger class

Comment on lines 438 to 456
def create_or_update_active_trigger(self, active_trigger_price: decimal.Decimal, active_trigger_above: bool):
if self.active_trigger is None:
self.active_trigger = price_trigger.PriceTrigger(
self.on_active_trigger, (None, None), active_trigger_price, active_trigger_above
)
else:
self.active_trigger.trigger_price = active_trigger_price
self.active_trigger.trigger_above = active_trigger_above

async def set_as_inactive(self, active_trigger_price: decimal.Decimal, active_trigger_above: bool):
"""
Marks the instance as inactive and ensures the inactive order watcher is scheduled.
"""
if active_trigger_price is None or active_trigger_above is None:
raise ValueError(
f"Both active_trigger_price and active_trigger_above must be provided to set an order as inactive"
)
logging.get_logger(self.get_logger_name()).info("Order is switching to inactive")
self.is_active = False
self.create_or_update_active_trigger(active_trigger_price, active_trigger_above)
# enforce attributes in case order has been canceled
self.status = enums.OrderStatus.OPEN
self.canceled_time = 0
await self._ensure_inactive_order_watcher()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is consistent with the trigger architecture. I believe Order should only be able to add/update a trigger but not create it. In other words, it should not take an 'active_trigger_price' argument but only an 'active_trigger,' and it’s the responsibility of the caller to provide the instance of the correct trigger class based on what is desired.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, i'll update this, thanks !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's up

import octobot_trading.personal_data.orders.triggers.base_trigger as base_trigger


class PriceTrigger(base_trigger.BaseTrigger):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

finally:
self.is_in_active_inactive_transition = previous_value

def use_active_trigger(self, active_trigger: price_trigger.PriceTrigger):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def use_active_trigger(self, active_trigger: price_trigger.PriceTrigger):
def use_active_trigger(self, active_trigger: base_trigger.BaseTrigger):

self.active_trigger.trigger_price = active_trigger.trigger_price
self.active_trigger.trigger_above = active_trigger.trigger_above

async def set_as_inactive(self, active_trigger: price_trigger.PriceTrigger):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async def set_as_inactive(self, active_trigger: price_trigger.PriceTrigger):
async def set_as_inactive(self, active_trigger: base_trigger.BaseTrigger):

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, it's up, I also added unit tests.

@GuillaumeDSM GuillaumeDSM changed the title WIP Inactive orders Inactive orders May 4, 2025
Copy link
Member

@Herklos Herklos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great feature!

def test_is_pending_when_not_initialized(trigger):
assert not trigger.is_pending()

def test_str_representation(trigger):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants