Skip to content
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

feat: add abandoned_time_limit to PublisherStalledCheck #74

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ignore_missing_imports = true

[tool.poetry]
name = "pyth-observer"
version = "0.2.9"
version = "0.2.10"
description = "Alerts and stuff"
authors = []
readme = "README.md"
Expand Down
13 changes: 8 additions & 5 deletions pyth_observer/check/publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def error_message(self) -> dict:
"symbol": self.__state.symbol,
"publisher_price": f"{self.__state.price} ± {self.__state.confidence_interval}",
"aggregate_price": f"{self.__state.price_aggregate} ± {self.__state.confidence_interval_aggregate}",
"deviation": deviation,
"deviation": f"{deviation:.2f}%",
Copy link
Contributor Author

@cctdaniel cctdaniel May 22, 2024

Choose a reason for hiding this comment

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

drive-by: format string here instead of event.py which is cleaner

}

# Returns the distance between the aggregate price and the closest side of the publisher's confidence interval
Expand All @@ -237,6 +237,7 @@ def __init__(self, state: PublisherState, config: PublisherCheckConfig):
self.__stall_time_limit: int = int(
config["stall_time_limit"]
) # Time in seconds
self.__abandoned_time_limit: int = int(config["abandoned_time_limit"])
self.__max_slot_distance: int = int(config["max_slot_distance"])

def state(self) -> PublisherState:
Expand All @@ -258,7 +259,7 @@ def run(self) -> bool:
return True

publisher_key = (self.__state.publisher_name, self.__state.symbol)
current_time = time.time()
current_time = int(time.time())
previous_price, last_change_time = PUBLISHER_CACHE.get(
publisher_key, (None, None)
)
Expand All @@ -267,7 +268,10 @@ def run(self) -> bool:
PUBLISHER_CACHE[publisher_key] = (self.__state.price, current_time)
return True

if (current_time - last_change_time) > self.__stall_time_limit:
time_since_last_change = current_time - last_change_time
if time_since_last_change > self.__stall_time_limit:
if time_since_last_change > self.__abandoned_time_limit:
return True # Abandon this check after the abandoned time limit
return False

return True
Expand All @@ -279,8 +283,7 @@ def error_message(self) -> dict:
"publisher": self.__state.publisher_name,
"symbol": self.__state.symbol,
"price": self.__state.price,
"stall_duration": time.time()
- PUBLISHER_CACHE[(self.__state.publisher_name, self.__state.symbol)][1],
"stall_duration": f"{int(time.time()) - PUBLISHER_CACHE[(self.__state.publisher_name, self.__state.symbol)][1]} seconds",
}


Expand Down
3 changes: 1 addition & 2 deletions pyth_observer/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,8 @@ async def send(self):

formatted_message = ""
for key, value in text.items():
value_str = f"{value:.2f}%" if key == "deviation" else f"{value}"
formatted_message += (
f"*{key.capitalize().replace('_', ' ')}:* {value_str}\n"
f"*{key.capitalize().replace('_', ' ')}:* {value}\n"
)

message_data = {
Expand Down
8 changes: 5 additions & 3 deletions sample.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ events:
# NOTE: Uncomment to enable Datadog metrics, see README.md for datadog credential docs.
# - DatadogEvent
- LogEvent
- TelegramEvent
# - TelegramEvent
checks:
global:
# Price feed checks
Expand Down Expand Up @@ -46,7 +46,8 @@ checks:
max_aggregate_distance: 6
PublisherStalledCheck:
enable: true
Copy link
Contributor

@ali-bahjati ali-bahjati May 22, 2024

Choose a reason for hiding this comment

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

Setting enable to false wouldn't work?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well the reason for this is to test it for a couple of symbols first (since the less liquid ones are noisy), if we set it to false then all will be disabled

Copy link
Contributor

@ali-bahjati ali-bahjati May 22, 2024

Choose a reason for hiding this comment

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

Hmmm i think this is because if this be false then other checks should override enable and sat enable: true. But that's clear now.

stall_time_limit: 60
stall_time_limit: 30000000 # arbitrary large number to "disable" this check while allowing per feed config to override
abandoned_time_limit: 30000000 # arbitrary large number to "disable" this check while allowing per feed config to override
max_slot_distance: 25
# Per-symbol config
Crypto.MNGO/USD:
Expand All @@ -57,4 +58,5 @@ checks:
max_slot_distance: 10000
Crypto.BTC/USD:
PublisherStalledCheck:
stall_time_limit: 10 # This will override the global stall_time_limit for Crypto.BTC/USD
stall_time_limit: 30 # This will override the global stall_time_limit for Crypto.BTC/USD
abandoned_time_limit: 600 # 10 minutes
11 changes: 6 additions & 5 deletions tests/test_checks_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@ def simulate_time_pass(seconds):
current_time += seconds
return current_time

def setup_check(state, stall_time_limit, max_slot_distance):
def setup_check(state, stall_time_limit, abandoned_time_limit, max_slot_distance):
check = PublisherStalledCheck(
state,
{
"stall_time_limit": stall_time_limit,
"abandoned_time_limit": abandoned_time_limit,
"max_slot_distance": max_slot_distance,
},
)
Expand All @@ -83,17 +84,17 @@ def run_check(check, seconds, expected):

PUBLISHER_CACHE.clear()
state_a = make_state(1, 100.0, 2.0, 1, 100.0, 1.0)
check_a = setup_check(state_a, 5, 25)
check_a = setup_check(state_a, 5, 25, 25)
run_check(check_a, 5, True) # Should pass as it hits the limit exactly

PUBLISHER_CACHE.clear()
state_b = make_state(1, 100.0, 2.0, 1, 100.0, 1.0)
check_b = setup_check(state_b, 5, 25)
check_b = setup_check(state_b, 5, 25, 25)
run_check(check_b, 6, False) # Should fail as it exceeds the limit

PUBLISHER_CACHE.clear()
state_c = make_state(1, 100.0, 2.0, 1, 100.0, 1.0)
check_c = setup_check(state_c, 5, 25)
check_c = setup_check(state_c, 5, 25, 25)
run_check(check_c, 2, True) # Initial check should pass
state_c.price = 105.0 # Change the price
run_check(check_c, 3, True) # Should pass as price changes
Expand All @@ -108,5 +109,5 @@ def run_check(check, seconds, expected):
state_d = make_state(1, 100.0, 2.0, 1, 100.0, 1.0)
state_d.latest_block_slot = 25
state_d.slot = 0
check_d = setup_check(state_d, 5, 25)
check_d = setup_check(state_d, 5, 25, 25)
run_check(check_d, 10, True) # Should pass as the publisher is offline
Loading