Skip to content

Commit 0d67ae1

Browse files
authored
feat(hip-3-pusher): Basic metrics, other fixes
2 parents 01b98f7 + e600293 commit 0d67ae1

File tree

6 files changed

+183
-34
lines changed

6 files changed

+183
-34
lines changed

apps/hip-3-pusher/config/config.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
stale_price_threshold_seconds = 5
2+
prometheus_port = 9090
23

34
[hyperliquid]
45
market_name = ""
@@ -15,7 +16,7 @@ aws_region_name = "ap-northeast-1"
1516

1617
[lazer]
1718
lazer_urls = ["wss://pyth-lazer-0.dourolabs.app/v1/stream", "wss://pyth-lazer-1.dourolabs.app/v1/stream"]
18-
api_key = "lazer_api_key"
19+
lazer_api_key = "lazer_api_key"
1920
base_feed_id = 1 # BTC
2021
base_feed_exponent = -8
2122
quote_feed_id = 8 # USDT

apps/hip-3-pusher/pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "hip-3-pusher"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
description = "Hyperliquid HIP-3 market oracle pusher"
55
readme = "README.md"
66
requires-python = ">=3.13"
@@ -10,6 +10,9 @@ dependencies = [
1010
"cryptography>=45.0.7",
1111
"hyperliquid-python-sdk>=0.19.0",
1212
"loguru>=0.7.3",
13+
"opentelemetry-exporter-prometheus>=0.58b0",
14+
"opentelemetry-sdk>=1.37.0",
15+
"prometheus-client>=0.23.1",
1316
"toml>=0.10.2",
1417
"websockets>=15.0.1",
1518
]

apps/hip-3-pusher/src/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from hermes_listener import HermesListener
1111
from price_state import PriceState
1212
from publisher import Publisher
13+
from metrics import Metrics
1314

1415

1516
def load_config():
@@ -38,7 +39,9 @@ async def main():
3839
config = load_config()
3940

4041
price_state = PriceState(config)
41-
publisher = Publisher(config, price_state)
42+
metrics = Metrics(config)
43+
44+
publisher = Publisher(config, price_state, metrics)
4245
hyperliquid_listener = HyperliquidListener(config, price_state)
4346
lazer_listener = LazerListener(config, price_state)
4447
hermes_listener = HermesListener(config, price_state)

apps/hip-3-pusher/src/metrics.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from prometheus_client import start_http_server
2+
from opentelemetry.exporter.prometheus import PrometheusMetricReader
3+
from opentelemetry.metrics import get_meter_provider, set_meter_provider
4+
from opentelemetry.sdk.metrics import MeterProvider
5+
6+
METER_NAME = "hip3pusher"
7+
8+
9+
class Metrics:
10+
def __init__(self, config):
11+
# Adapted from opentelemetry-exporter-prometheus example code.
12+
# Start Prometheus client
13+
start_http_server(port=config["prometheus_port"], addr="localhost")
14+
# Exporter to export metrics to Prometheus
15+
reader = PrometheusMetricReader()
16+
# Meter is responsible for creating and recording metrics
17+
set_meter_provider(MeterProvider(metric_readers=[reader]))
18+
# TODO: sync version number and add?
19+
self.meter = get_meter_provider().get_meter(METER_NAME)
20+
21+
self._init_metrics()
22+
23+
def _init_metrics(self):
24+
self.no_oracle_price_counter = self.meter.create_counter(
25+
name="hip_3_pusher_no_oracle_price_count",
26+
description="Number of failed push attempts with no valid oracle price",
27+
)
28+
self.successful_push_counter = self.meter.create_counter(
29+
name="hip_3_pusher_successful_push_count",
30+
description="Number of successful push attempts",
31+
)
32+
self.failed_push_counter = self.meter.create_counter(
33+
name="hip_3_pusher_failed_push_count",
34+
description="Number of failed push attempts",
35+
)
36+
37+
# TODO: labels/attributes

apps/hip-3-pusher/src/publisher.py

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from hyperliquid.utils.constants import TESTNET_API_URL, MAINNET_API_URL
99

1010
from kms_signer import KMSSigner
11+
from metrics import Metrics
1112
from price_state import PriceState
1213

1314

@@ -17,7 +18,7 @@ class Publisher:
1718
1819
See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/hip-3-deployer-actions
1920
"""
20-
def __init__(self, config: dict, price_state: PriceState):
21+
def __init__(self, config: dict, price_state: PriceState, metrics: Metrics):
2122
self.publish_interval = float(config["hyperliquid"]["publish_interval"])
2223
self.kms_signer = None
2324
self.enable_kms = False
@@ -43,40 +44,54 @@ def __init__(self, config: dict, price_state: PriceState):
4344
self.enable_publish = config["hyperliquid"].get("enable_publish", False)
4445

4546
self.price_state = price_state
47+
self.metrics = metrics
4648

4749
async def run(self):
4850
while True:
4951
await asyncio.sleep(self.publish_interval)
52+
try:
53+
self.publish()
54+
except Exception as e:
55+
logger.exception("Publisher.publish() exception: {}", e)
5056

51-
oracle_pxs = {}
52-
oracle_px = self.price_state.get_current_oracle_price()
53-
if not oracle_px:
54-
logger.error("No valid oracle price available!")
55-
return
56-
else:
57-
logger.debug("Current oracle price: {}", oracle_px)
58-
oracle_pxs[self.market_symbol] = oracle_px
57+
def publish(self):
58+
oracle_pxs = {}
59+
oracle_px = self.price_state.get_current_oracle_price()
60+
if not oracle_px:
61+
logger.error("No valid oracle price available")
62+
self.metrics.no_oracle_price_counter.add(1)
63+
return
64+
else:
65+
logger.debug("Current oracle price: {}", oracle_px)
66+
oracle_pxs[self.market_symbol] = oracle_px
5967

60-
mark_pxs = []
61-
#if self.price_state.hl_mark_price:
62-
# mark_pxs.append({self.market_symbol: self.price_state.hl_mark_price})
68+
mark_pxs = []
69+
#if self.price_state.hl_mark_price:
70+
# mark_pxs.append({self.market_symbol: self.price_state.hl_mark_price})
6371

64-
external_perp_pxs = {}
72+
external_perp_pxs = {}
73+
74+
if self.enable_publish:
75+
if self.enable_kms:
76+
push_response = self.kms_signer.set_oracle(
77+
dex=self.market_name,
78+
oracle_pxs=oracle_pxs,
79+
all_mark_pxs=mark_pxs,
80+
external_perp_pxs=external_perp_pxs,
81+
)
82+
else:
83+
push_response = self.oracle_publisher_exchange.perp_deploy_set_oracle(
84+
dex=self.market_name,
85+
oracle_pxs=oracle_pxs,
86+
all_mark_pxs=mark_pxs,
87+
external_perp_pxs=external_perp_pxs,
88+
)
6589

66-
if self.enable_publish:
67-
if self.enable_kms:
68-
push_response = self.kms_signer.set_oracle(
69-
dex=self.market_name,
70-
oracle_pxs=oracle_pxs,
71-
all_mark_pxs=mark_pxs,
72-
external_perp_pxs=external_perp_pxs,
73-
)
74-
else:
75-
push_response = self.oracle_publisher_exchange.perp_deploy_set_oracle(
76-
dex=self.market_name,
77-
oracle_pxs=oracle_pxs,
78-
all_mark_pxs=mark_pxs,
79-
external_perp_pxs=external_perp_pxs,
80-
)
81-
# TODO: Look at specific error responses and log/alert accordingly
82-
logger.info("publish: push response: {}", push_response)
90+
# TODO: Look at specific error responses and log/alert accordingly
91+
logger.debug("publish: push response: {} {}", push_response, type(push_response))
92+
status = push_response.get("status", "")
93+
if status == "ok":
94+
self.metrics.successful_push_counter.add(1)
95+
elif status == "err":
96+
self.metrics.failed_push_counter.add(1)
97+
logger.error("publish: publish error: {}", push_response)

apps/hip-3-pusher/uv.lock

Lines changed: 91 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)