33import time
44
55from pusher .config import Config , PriceSource , PriceSourceConfig , ConstantSourceConfig , SingleSourceConfig , \
6- PairSourceConfig
6+ PairSourceConfig , OracleMidAverageConfig
77
88DEFAULT_STALE_PRICE_THRESHOLD_SECONDS = 5
99
@@ -17,6 +17,13 @@ def time_diff(self, now):
1717 return now - self .timestamp
1818
1919
20+ @dataclass
21+ class OracleUpdate :
22+ oracle : dict [str , str ]
23+ mark : dict [str , str ]
24+ external : dict [str , str ]
25+
26+
2027class PriceSourceState :
2128 def __init__ (self , name : str ):
2229 self .name = name
@@ -35,6 +42,7 @@ def put(self, symbol: str, value: PriceUpdate):
3542class PriceState :
3643 HL_ORACLE = "hl_oracle"
3744 HL_MARK = "hl_mark"
45+ HL_MID = "hl_mid"
3846 LAZER = "lazer"
3947 HERMES = "hermes"
4048 SEDA = "seda"
@@ -43,50 +51,56 @@ class PriceState:
4351 Maintain latest prices seen across listeners and publisher.
4452 """
4553 def __init__ (self , config : Config ):
54+ self .market_name = config .hyperliquid .market_name
4655 self .stale_price_threshold_seconds = config .stale_price_threshold_seconds
4756 self .price_config = config .price
4857
4958 self .hl_oracle_state = PriceSourceState (self .HL_ORACLE )
5059 self .hl_mark_state = PriceSourceState (self .HL_MARK )
60+ self .hl_mid_state = PriceSourceState (self .HL_MID )
5161 self .lazer_state = PriceSourceState (self .LAZER )
5262 self .hermes_state = PriceSourceState (self .HERMES )
5363 self .seda_state = PriceSourceState (self .SEDA )
5464
5565 self .all_states = {
5666 self .HL_ORACLE : self .hl_oracle_state ,
5767 self .HL_MARK : self .hl_mark_state ,
68+ self .HL_MID : self .hl_mid_state ,
5869 self .LAZER : self .lazer_state ,
5970 self .HERMES : self .hermes_state ,
6071 self .SEDA : self .seda_state ,
6172 }
6273
63- def get_all_prices (self , market_name ) :
74+ def get_all_prices (self ) -> OracleUpdate :
6475 logger .debug ("get_all_prices state: {}" , self .all_states )
6576
66- return (
67- self .get_prices (self .price_config .oracle , market_name ),
68- self .get_prices (self .price_config .mark , market_name ),
69- self .get_prices (self .price_config .external , market_name )
70- )
77+ oracle_update = OracleUpdate ({}, {}, {})
78+ oracle_update .oracle = self .get_prices (self .price_config .oracle , oracle_update )
79+ oracle_update .mark = self .get_prices (self .price_config .mark , oracle_update )
80+ oracle_update .external = self .get_prices (self .price_config .external , oracle_update )
7181
72- def get_prices (self , symbol_configs : dict [str , list [PriceSourceConfig ]], market_name : str ):
82+ return oracle_update
83+
84+ def get_prices (self , symbol_configs : dict [str , list [PriceSourceConfig ]], oracle_update : OracleUpdate ):
7385 pxs = {}
7486 for symbol in symbol_configs :
7587 for source_config in symbol_configs [symbol ]:
7688 # find first valid price in the waterfall
77- px = self .get_price (source_config )
89+ px = self .get_price (source_config , oracle_update )
7890 if px is not None :
79- pxs [f"{ market_name } :{ symbol } " ] = px
91+ pxs [f"{ self . market_name } :{ symbol } " ] = str ( px )
8092 break
8193 return pxs
8294
83- def get_price (self , price_source_config : PriceSourceConfig ):
95+ def get_price (self , price_source_config : PriceSourceConfig , oracle_update : OracleUpdate ):
8496 if isinstance (price_source_config , ConstantSourceConfig ):
8597 return price_source_config .value
8698 elif isinstance (price_source_config , SingleSourceConfig ):
8799 return self .get_price_from_single_source (price_source_config .source )
88100 elif isinstance (price_source_config , PairSourceConfig ):
89101 return self .get_price_from_pair_source (price_source_config .base_source , price_source_config .quote_source )
102+ elif isinstance (price_source_config , OracleMidAverageConfig ):
103+ return self .get_price_from_oracle_mid_average (price_source_config .symbol , oracle_update )
90104 else :
91105 raise ValueError
92106
@@ -115,3 +129,19 @@ def get_price_from_pair_source(self, base_source: PriceSource, quote_source: Pri
115129 return None
116130
117131 return str (round (float (base_price ) / float (quote_price ), 2 ))
132+
133+ def get_price_from_oracle_mid_average (self , symbol : str , oracle_update : OracleUpdate ):
134+ oracle_price = oracle_update .oracle .get (symbol )
135+ if oracle_price is None :
136+ return None
137+
138+ mid_price_update : PriceUpdate | None = self .hl_mid_state .get (symbol )
139+ if mid_price_update is None :
140+ logger .warning ("mid price for {} is missing" , symbol )
141+ return None
142+ time_diff = mid_price_update .time_diff (time .time ())
143+ if time_diff >= self .stale_price_threshold_seconds :
144+ logger .warning ("mid price for {} is stale by {} seconds" , symbol , time_diff )
145+ return None
146+
147+ return (float (oracle_price ) + float (mid_price_update .price )) / 2.0
0 commit comments