@@ -18,6 +18,7 @@ The above copyright notice and this permission notice shall be included in all c
1818using System . Text ;
1919using System . Threading . Tasks ;
2020using System . Web ;
21+ using System . Xml ;
2122
2223using Newtonsoft . Json ;
2324using Newtonsoft . Json . Linq ;
@@ -96,10 +97,88 @@ protected override async Task<IEnumerable<string>> OnGetMarketSymbolsAsync()
9697
9798 protected internal override async Task < IEnumerable < ExchangeMarket > > OnGetMarketSymbolsMetadataAsync ( )
9899 {
99- Logger . Warn ( "Fetching gemini symbol metadata, this may take a minute..." ) ;
100+ List < ExchangeMarket > markets = new List < ExchangeMarket > ( ) ;
101+
102+ try
103+ {
104+ string html = await RequestMaker . MakeRequestAsync ( "/rest-api" , "https://docs.gemini.com" ) ;
105+ int startPos = html . IndexOf ( "<h1 id=\" symbols-and-minimums\" >Symbols and minimums</h1>" ) ;
106+ if ( startPos >= 0 )
107+ {
108+ startPos = html . IndexOf ( "<tbody>" , startPos ) ;
109+ if ( startPos >= 0 )
110+ {
111+ int endPos = html . IndexOf ( "</tbody>" , startPos ) ;
112+ string table = html . Substring ( startPos , endPos - startPos + "</tbody>" . Length ) ;
113+ string xml = "<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>\n " + table ;
114+ XmlDocument doc = new XmlDocument ( ) ;
115+ doc . LoadXml ( xml ) ;
116+ if ( doc . ChildNodes . Count > 1 )
117+ {
118+ XmlNode root = doc . ChildNodes . Item ( 1 ) ;
119+ foreach ( XmlNode tr in root . ChildNodes )
120+ {
121+ // <tr>
122+ // <th>Symbol</th>
123+ // <th>Minimum Order Size</th>
124+ // <th>Tick Size</th>
125+ // <th>Quote Currency Price Increment</th>
126+
127+ // <td>btcusd</td>
128+ // <td>0.00001 BTC (1e-5)</td>
129+ // <td>0.00000001 BTC (1e-8)</td>
130+ // <td>0.01 USD</td>
131+ // </tr>
132+
133+ if ( tr . ChildNodes . Count == 4 )
134+ {
135+ ExchangeMarket market = new ExchangeMarket { IsActive = true } ;
136+ XmlNode symbolNode = tr . ChildNodes . Item ( 0 ) ;
137+ XmlNode minOrderSizeNode = tr . ChildNodes . Item ( 1 ) ;
138+ XmlNode tickSizeNode = tr . ChildNodes . Item ( 2 ) ;
139+ XmlNode incrementNode = tr . ChildNodes . Item ( 3 ) ;
140+ string symbol = symbolNode . InnerText ;
141+ int minOrderSizePos = minOrderSizeNode . InnerText . IndexOf ( ' ' ) ;
142+ if ( minOrderSizePos < 0 )
143+ {
144+ throw new ArgumentException ( "Min order size text does not have a space after the number" ) ;
145+ }
146+ decimal minOrderSize = minOrderSizeNode . InnerText . Substring ( 0 , minOrderSizePos ) . ConvertInvariant < decimal > ( ) ;
147+ int tickSizePos = tickSizeNode . InnerText . IndexOf ( ' ' ) ;
148+ if ( tickSizePos < 0 )
149+ {
150+ throw new ArgumentException ( "Tick size text does not have a space after the number" ) ;
151+ }
152+ decimal tickSize = tickSizeNode . InnerText . Substring ( 0 , tickSizePos ) . ConvertInvariant < decimal > ( ) ;
153+ int incrementSizePos = incrementNode . InnerText . IndexOf ( ' ' ) ;
154+ if ( incrementSizePos < 0 )
155+ {
156+ throw new ArgumentException ( "Increment size text does not have a space after the number" ) ;
157+ }
158+ decimal incrementSize = incrementNode . InnerText . Substring ( 0 , incrementSizePos ) . ConvertInvariant < decimal > ( ) ;
159+ market . MarketSymbol = symbol ;
160+ market . BaseCurrency = symbol . Substring ( 0 , symbol . Length - 3 ) ;
161+ market . QuoteCurrency = symbol . Substring ( symbol . Length - 3 ) ;
162+ market . MinTradeSize = minOrderSize ;
163+ market . QuantityStepSize = tickSize ;
164+ market . PriceStepSize = incrementSize ;
165+ markets . Add ( market ) ;
166+ }
167+ }
168+ return markets ;
169+ }
170+ }
171+ }
172+ }
173+ catch ( Exception ex )
174+ {
175+ Logger . Error ( ex , "Failed to parse gemini symbol metadata web page, falling back to per symbol query..." ) ;
176+ }
177+
178+ // slow way, fetch each symbol one by one, gemini api epic fail
179+ Logger . Warn ( "Fetching gemini symbol metadata per symbol, this may take a minute..." ) ;
100180
101181 string [ ] symbols = ( await GetMarketSymbolsAsync ( ) ) . ToArray ( ) ;
102- List < ExchangeMarket > markets = new List < ExchangeMarket > ( ) ;
103182 List < Task > tasks = new List < Task > ( ) ;
104183 foreach ( string symbol in symbols )
105184 {
@@ -115,7 +194,7 @@ protected internal override async Task<IEnumerable<ExchangeMarket>> OnGetMarketS
115194 BaseCurrency = token [ "base_currency" ] . ToStringInvariant ( ) ,
116195 IsActive = token [ "status" ] . ToStringInvariant ( ) . Equals ( "open" , StringComparison . OrdinalIgnoreCase ) ,
117196 MarketSymbol = token [ "symbol" ] . ToStringInvariant ( ) ,
118- MinTradeSize = token [ "min_order_Size " ] . ConvertInvariant < decimal > ( ) ,
197+ MinTradeSize = token [ "min_order_size " ] . ConvertInvariant < decimal > ( ) ,
119198 QuantityStepSize = token [ "tick_size" ] . ConvertInvariant < decimal > ( ) ,
120199 QuoteCurrency = token [ "quote_currency" ] . ToStringInvariant ( ) ,
121200 PriceStepSize = token [ "quote_increment" ] . ConvertInvariant < decimal > ( )
0 commit comments