11from decimal import Decimal
2+ from math import floor
3+
4+ import grpc
5+
6+ import injective .exchange_api .injective_spot_exchange_rpc_pb2 as spot_exchange_rpc_pb
7+ import injective .exchange_api .injective_spot_exchange_rpc_pb2_grpc as spot_exchange_rpc_grpc
8+ import injective .exchange_api .injective_derivative_exchange_rpc_pb2 as derivative_exchange_rpc_pb
9+ import injective .exchange_api .injective_derivative_exchange_rpc_pb2_grpc as derivative_exchange_rpc_grpc
10+
211"""
3- One thing you may need to pay more attention to is how to deal with decimals in injective exchange.
4- As we all known, different crypto currecies require diffrent decimal precisions.
5- Separately, ERC-20 tokens(e.g. INJ) have decimals of 18 or another number(like 6 for USDT and USDC).
6- So in injective system that means ** having 1 INJ is 1e18 inj ** and that ** 1 USDT is actually 100000 peggy0xdac17f958d2ee523a2206206994597c13d831ec7**.
12+ One thing you may need to pay more attention to is how to deal with decimals on the Injective Exchange.
13+ Different cryptocurrencies may require diffrent decimal precisions. More specifically, ERC-20 tokens(e.g. INJ) have 18 decimals whereas USDT/USC have 6 decimals.
14+ So in our system that means ** having 1 INJ is 1e18 inj ** and that ** 1 USDT is actually 100000 peggy0xdac17f958d2ee523a2206206994597c13d831ec7**.
715
816For spot markets, a price reflects the ** relative exchange rate ** between two tokens.
9- If the tokens have the same decimal scale, that's great since the prices
10- become interpretable e.g. USDT/USDC (both have 6 decimals e.g. for USDT https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#readContract)
17+ If the tokens have the same decimal scale, that's great since the prices become interpretable e.g. USDT/USDC (both have 6 decimals e.g. for USDT https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#readContract)
1118or MATIC/INJ (both have 18 decimals) since the decimals cancel out.
12- Prices however start to look wonky once you have exchanges between two tokens of different decimals, which unfortunately is most pairs with USDT or USDC denominations.
13- As such, I've created some simple utility functions by keeping a hardcoded dictionary in injective-py and you can aslo achieve such utilities by yourself
19+ Prices however start to look wonky once you have exchanges between two tokens of different decimals, which unfortunately is most pairs with USDT or USDC denominations e.g. INJ/USDT .
20+ As such, I've created some simple utility functions by keeping a hardcoded dictionary in injective-py and you can also achieve such utilities by yourself
1421(e.g. you can use external API like Alchemy's getTokenMetadata to fetch decimal of base and quote asset).
1522
16- So for INJ/USDT of 6.9, the price you end up getting is 6.9*10 ^ (6 - 18) = 6.9e-12.
17- Note that this market also happens to have a MinPriceTickSize of 1e-15 .
18- This makes sense since since it's defining the minimum price increment of the relative exchange of INJ to USDT.
23+ So for INJ/USDT of 6.9, the price you end up getting is 6.9*10 ^ (6 - 18) = 6.9e-12. Note that this market also happens to have a MinPriceTickSize of 1e-15.
24+ This makes sense since since it's defining th3e minimum price increment of the relative exchange of INJ to USDT .
25+
1926Note that this market also happens to have a MinQuantityTickSize of 1e15.
2027This also makes sense since it refers to the minimum INJ quantity tick size each order must have, which is 1e15/1e18 = 0.001 INJ.
28+
2129"""
22- def price_float_to_string (price , base_decimals , quote_decimals , precision = 18 ) -> str :
23- """transfer price[float] to string which satisfies what injective exchange backend requires
2430
31+ async def spot_price_to_backend (endpoint , market , price , base_decimals , quote_decimals , precision = 18 ) -> str :
32+
33+ """
2534 Args:
35+ endpoint ([string]): the endpoint for the gRPC request
36+ market ([string]): the market_id of the pair
2637 price ([float]): normal price, you can read it directly in exchange front-end
27- base_decimals ([int]): decimal of base asset
28- quote_decimals ([int]): quote asset's decimal
38+ base_decimals ([int]): decimals of base asset
39+ quote_decimals ([int]): decimals of quote asset
2940 precision (int, optional): [description]. Defaults to 18.
41+
3042 Returns:
3143 str: relative price for base asset and quote asset. For INJ/USDT of 6.9, the price you end up getting is 6.9*10 ^ (6 - 18) = 6.9e-12.
3244 """
33- scale = Decimal (quote_decimals - base_decimals )
34- exchange_price = Decimal (price ) * pow (10 , scale )
45+
46+ async with grpc .aio .insecure_channel (endpoint ) as channel :
47+ spot_exchange_rpc = spot_exchange_rpc_grpc .InjectiveSpotExchangeRPCStub (channel )
48+ mresp = await spot_exchange_rpc .Market (spot_exchange_rpc_pb .MarketRequest (market_id = market ))
49+
50+ scale_tick_size = int (base_decimals - quote_decimals )
51+ price_tick_size = float (mresp .market .min_price_tick_size ) * pow (10 , scale_tick_size )
52+ scale_price = Decimal (quote_decimals - base_decimals )
53+ exchange_price = floor_to (price , price_tick_size ) * pow (10 , scale_price )
3554 price_string = ("{:." + str (precision )+ "f}" ).format (exchange_price )
3655 print ("price string :{}" .format (price_string ))
3756 return price_string
3857
58+ async def derivative_price_to_backend (endpoint , market , price , quote_decimals , precision = 18 ) -> str :
3959
40- def quantity_float_to_string (quantity , base_decimals , precision = 18 ) -> str :
41- """transfer quantity[float] to string which satisfies what injective exchange backend requires
60+ """
61+ Args:
62+ endpoint ([string]): the endpoint for the gRPC request
63+ market ([string]): the market_id of the pair
64+ price ([float]): normal price, you can read it directly in exchange front-end
65+ quote_decimals ([int]): decimals of quote asset
66+ precision (int, optional): [description]. Defaults to 18.
4267
68+ Returns:
69+ str: relative price for the quote asset. For INJ/USDT of 6.9, the price you end up getting is 6.9*10 ^ (6) = 6.9e6.
70+ """
71+
72+ async with grpc .aio .insecure_channel (endpoint ) as channel :
73+ derivative_exchange_rpc = derivative_exchange_rpc_grpc .InjectiveDerivativeExchangeRPCStub (channel )
74+ mresp = await derivative_exchange_rpc .Market (derivative_exchange_rpc_pb .MarketRequest (market_id = market ))
75+
76+
77+ scale = int (0 - quote_decimals )
78+ price_tick_size = float (mresp .market .min_price_tick_size ) * pow (10 , scale )
79+ exchange_price = floor_to (price , price_tick_size ) * pow (10 , quote_decimals )
80+ price_string = ("{:." + str (precision )+ "f}" ).format (exchange_price )
81+ print ("price string :{}" .format (price_string ))
82+ return price_string
83+
84+ async def derivative_margin_to_backend (endpoint , market , price , quantity , leverage , quote_decimals , precision = 18 ) -> str :
85+
86+ """
4387 Args:
44- quantity ([type]): normal quantity, you can read it in exchange front-end
45- base_decimals ([type]): decimal of base asset
88+ endpoint ([string]): the endpoint for the gRPC request
89+ market ([string]): the market_id of the pair
90+ price ([float]): normal price, you can read it directly in exchange front-end
91+ quantity ([float]): normal quantity, you can read it directly in exchange front-end
92+ leverage ([float]): normal leverage, you can read it directly in exchange front-end
93+ quote_decimals ([int]): decimals of quote asset
94+ precision (int, optional): [description]. Defaults to 18.
95+
96+ Returns:
97+ str: relative price for the quote asset. For INJ/USDT of 6.9, the price you end up getting is 6.9*10 ^ (6) = 6.9e6.
98+ """
99+
100+ async with grpc .aio .insecure_channel (endpoint ) as channel :
101+ derivative_exchange_rpc = derivative_exchange_rpc_grpc .InjectiveDerivativeExchangeRPCStub (channel )
102+ mresp = await derivative_exchange_rpc .Market (derivative_exchange_rpc_pb .MarketRequest (market_id = market ))
103+
104+ scale = int (0 - quote_decimals )
105+ price_tick_size = float (mresp .market .min_price_tick_size ) * pow (10 , scale )
106+ margin = (price * quantity ) / leverage
107+ exchange_margin = floor_to (margin , price_tick_size ) * pow (10 , quote_decimals )
108+ price_string = ("{:." + str (precision )+ "f}" ).format (exchange_margin )
109+ print ("margin string :{}" .format (price_string ))
110+ return price_string
111+
112+
113+ async def spot_quantity_to_backend (endpoint , market , quantity , base_decimals , precision = 18 ) -> str :
114+
115+ """
116+ Args:
117+ endpoint ([string]): the endpoint for the gRPC request
118+ market ([string]): the market_id of the pair
119+ quantity ([float]): normal quantity, you can read it in exchange front-end
120+ base_decimals ([int]): decimals of base asset
46121 precision (int, optional): [description]. Defaults to 18.
47122
48123 Returns:
49- str: acutally quanity of base asset[data type: string] For 1 INJ, the quantity you end up is 1e18 inj
124+ str: actual quantity of base asset[data type: string] For 1 INJ, the quantity you end up getting is 1e18 inj
50125 """
51- scale = Decimal (base_decimals )
52- exchange_quantity = Decimal (quantity ) * pow (10 , scale )
126+
127+ async with grpc .aio .insecure_channel (endpoint ) as channel :
128+ spot_exchange_rpc = spot_exchange_rpc_grpc .InjectiveSpotExchangeRPCStub (channel )
129+ mresp = await spot_exchange_rpc .Market (spot_exchange_rpc_pb .MarketRequest (market_id = market ))
130+
131+ scale_tick_size = float (0 - base_decimals )
132+ quantity_tick_size = float (mresp .market .min_quantity_tick_size ) * pow (10 , scale_tick_size )
133+ scale_quantity = Decimal (base_decimals )
134+ exchange_quantity = floor_to (quantity , quantity_tick_size ) * pow (10 , scale_quantity )
53135 quantity_string = ("{:." + str (precision )+ "f}" ).format (exchange_quantity )
54136 print ("quantity string:{}" .format (quantity_string ))
55137 return quantity_string
56138
57139
58- def price_string_to_float (price_string , base_decimals , quote_decimals ) -> float :
140+ async def derivative_quantity_to_backend (endpoint , market , quantity , quote_decimals , precision = 18 ) -> str :
141+
59142 """
60143 Args:
61- price_string ([type]): price with string data type that injective-exchange backend returns
62- base_decimals ([type]): decimal of base asset
63- quote_decimals ([type]): decimal of quote asset
144+ endpoint ([string]): the endpoint for the gRPC request
145+ market ([string]): the market_id of the pair
146+ quantity ([float]): normal quantity, you can read it in exchange front-end
147+ quote_decimals ([int]): decimals of quote asset
148+ precision (int, optional): [description]. Defaults to 18.
149+
150+ Returns:
151+ str: actual quantity of quote asset[data type: string] For 1 USDT, the quantity you end up is 1.000000000000000000 USDT
152+ """
153+
154+ async with grpc .aio .insecure_channel (endpoint ) as channel :
155+ derivative_exchange_rpc = derivative_exchange_rpc_grpc .InjectiveDerivativeExchangeRPCStub (channel )
156+ mresp = await derivative_exchange_rpc .Market (derivative_exchange_rpc_pb .MarketRequest (market_id = market ))
157+
158+ quantity_tick_size = float (mresp .market .min_quantity_tick_size )
159+ exchange_quantity = floor_to (quantity , quantity_tick_size )
160+ quantity_string = ("{:." + str (precision )+ "f}" ).format (exchange_quantity )
161+ print ("quantity string :{}" .format (quantity_string ))
162+ return quantity_string
163+
164+ def spot_price_from_backend (price_string , base_decimals , quote_decimals ) -> float :
165+
166+ """
167+ Args:
168+ price_string ([string]): price with string data type that injective-exchange backend returns
169+ base_decimals ([int]): decimal of base asset
170+ quote_decimals ([int]): decimal of quote asset
64171
65172 Returns:
66173 float: actual price what you can read directly from front-end.
67174 For 6.9e-12 inj/peggy0xdac17f958d2ee523a2206206994597c13d831ec7**, the price you end up getting is 6.9 INJ/USDT.
68175 """
176+
69177 scale = float (base_decimals - quote_decimals )
70178 return float (price_string ) * pow (10 , scale )
71179
180+ async def spot_quantity_from_backend (endpoint , market , quantity_string , base_decimals ) -> float :
72181
73- def quantity_string_to_float (quantity_string , base_decimals ) -> float :
74182 """
75-
76183 Args:
77184 quantity_string ([type]): quantity string that injective-exchange backend returns
78185 base_decimals ([type]): decimal of base asset
186+ endpoint ([string]): the endpoint for the gRPC request
187+ market ([string]): the market_id of the pair
79188
80189 Returns:
81- float: actually quantity, for 1e18 inj, you will get 1 INJ
190+ float: actual quantity, for 1e18 inj, you will get 1 INJ
82191 """
192+ async with grpc .aio .insecure_channel (endpoint ) as channel :
193+ spot_exchange_rpc = spot_exchange_rpc_grpc .InjectiveSpotExchangeRPCStub (channel )
194+ mresp = await spot_exchange_rpc .Market (spot_exchange_rpc_pb .MarketRequest (market_id = market ))
195+
83196 scale = float (0 - base_decimals )
84- return float (quantity_string ) * pow (10 , scale )
197+ quantity_tick_size = float (mresp .market .min_quantity_tick_size ) * pow (10 , scale )
198+ quantity = float (quantity_string ) * pow (10 , scale )
199+ return floor_to (quantity , quantity_tick_size )
85200
86201
87- # test
88- if __name__ == "__main__" :
89- assert "0.000000005000000000" == price_float_to_string (
90- 5000 , 18 , 6 , 18 ), "result of get_price is wrong."
202+ def derivative_price_from_backend (price_string , quote_decimals = 6 ) -> float :
203+
204+ """
205+ Args:
206+ price_string ([string]): price with string data type that injective-exchange backend returns
207+ quote_decimals ([int]): decimals of quote asset. Defaults to 6
91208
92- assert "1222000000000000000000.000000000000000000" == quantity_float_to_string (
93- 1222 , 18 , 18 ), "result of get_quantity is wrong."
209+ Returns:
210+ float: actual price which you can read directly from the front-end.
211+ For 6.9e6 inj/peggy0xdac17f958d2ee523a2206206994597c13d831ec7**, the price you end up getting is 6.9 INJ/USDT.
212+ """
213+
214+ scale = float (0 - quote_decimals )
215+ return float (price_string ) * pow (10 , scale )
94216
95- assert 5000 == price_string_to_float (price_float_to_string (
96- 5000 , 18 , 6 , 18 ), 18 , 6 ), "something is wrong."
97217
98- assert 1222 == quantity_string_to_float (
99- quantity_float_to_string (1222 , 18 , 18 ), 18 ), "something is wrong."
218+ def floor_to (value : float , target : float ) -> str :
219+ value = Decimal (str (value ))
220+ target = Decimal (str (target ))
221+ result = Decimal (int (floor (value / target )) * target )
222+ return result
0 commit comments