001/* 002 * $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/GoogleFinanceScraper.java $ 003 * $Author: tgutwin $ 004 * $Revision: 1255 $ 005 * $Date: 2018-03-17 20:34:04 -0700 (Sat, 17 Mar 2018) $ 006 */ 007package ca.bc.webarts.tools; 008 009import java.util.HashMap; 010import java.util.Properties; 011import javax.json.JsonObject; 012import javax.json.JsonArray; 013import javax.json.JsonNumber; 014import javax.json.JsonString; 015 016import ca.bc.webarts.widgets.ResultSetConverter; 017 018import java.text.DecimalFormat; 019 020 021 /** 022 * The Google finance webpage scraper. It is a class that extends the UrlScraper class to wrap/abstract the login and 023 * credentials away and just focusses on the scraping of data from the restricted pages.<br><br> 024 * <a href="https://trading.credentialdirect.com">https://trading.credentialdirect.com</a> is a site requiring login/authentication, so this class provides autoLogin and scraping of sub-pages.<br> 025 * The site login credentials are NOT kept inside the code for this class; they are in a properties file called '<i>.credential</i>' in the '<i>user.home</i>' directory. 026 * <br>The properites that should be in this file:<ul><li>username</li><li>password</li></ul>Maybe the request properties for the website could go in here too.<br> 027 * <b>copyright (c) 2017-2018 Tom B. Gutwin</b> 028 **/ 029public class GoogleFinanceScraper extends UrlScraper 030{ 031 /** A holder for this clients System File Separator. */ 032 public final static String SYSTEM_FILE_SEPERATOR = java.io.File.separator; 033 034 /** A holder for this clients System line termination separator. */ 035 public final static String SYSTEM_LINE_SEPERATOR = 036 System.getProperty("line.separator"); 037 038 public final static String COL_DELIM = SqlQuery.DEFAULT_COLUMN_DELIMITOR; 039 public static final String QUOTE_SYMBOL_TOKEN = "^%SYMBOL%^"; 040 public static final String QUOTE_MARKET_TOKEN = "^%MARKET%^"; 041 042 /** The users home ditrectory. */ 043 public static String USERHOME = System.getProperty("user.home"); 044 045 private String googleFinanceFilename_ = USERHOME+SYSTEM_FILE_SEPERATOR+".googleFinance"; 046 047 HashMap <String, String> reqProps = new HashMap<String, String>(); 048 049 //https://login.google.com/?.src=fpctx&.intl=ca&.lang=en-CA&authMechanism=primary&yid=&done=https%3A%2F%2Fca.google.com%2F&eid=100&as=1&login=tgutwin&crumb=9D8I.8WDkTW 050 String gLoginUrl_plong = "https://login.google.com/account/challenge/password?.src=fpctx&.intl=ca&.lang=en-CA&authMechanism=primary&yid=tgutwin&done=https%3A%2F%2Ffinance.google.com%2F&as=1&login=tgutwin&crumb=1EAn939fa%2Ft&display=login&s=QQ--&sessionIndex=QQ--&acrumb=FBRp8xFN"; 051 String gLoginUrl_pshort = "https://login.google.com/?.intl=ca&.lang=en-CA&login=tgutwin&.src=fpctx&done=https%3A%2F%2Ffinance.google.com%2F&prefill=0"; //"https://login.google.com/account/challenge/password?.src=fpctx&.intl=ca&.lang=en-CA&authMechanism=primary&yid=tgutwin&done=https%3A%2F%2Ffinance.google.com%2F&as=1&login=tgutwin"; 052 String gLoginUrl = "https://login.google.com/?display=login&login=tgutwin&done=https%3A%2F%2Ffinance.google.com%2F&prefill=1"; //https://login.google.com/?authMechanism=primary&yid=&done=https%3A%2F%2Ffinance.google.com%2F&eid=100&as=1&login=tgutwin&crumb=9D8I.8WDkTW 053 String gScrapePageUrl = "https://finance.google.ca/finance?q=NASDAQ:ITRI&ei=KZITWpDrINihjAHU9JWIAg"; 054 static String gScrapeQuoteTokenizedUrl = "https://finance.google.ca/finance?q="+QUOTE_MARKET_TOKEN+":"+QUOTE_SYMBOL_TOKEN; 055 056 String gLoginFormElement = "pure-form pure-form-stacked"; /* id of the form element */ 057 String gUserLoginElement = "displayName"; 058 String gPasswordElement = "login-passwd"; 059 String gUsername = ""; // load from outside somewhere 060 String gPassword = ""; // load from outside somewhere 061 062 String gScrapeQuoteStart= "<div id=market-data-div"; 063 String gScrapeQuoteStart_= "<table><tr><td class=\"JgXcPd\">"; 064 String gScrapeQuoteEnd= "<script>google.finance.renderMarketData();</script>"; 065 String gScrapeQuoteEnd_= "</td></tr></table></div></div></div></g-card-section></div></div></div>"; 066 067 private JsonArray holdings = null; 068 private JsonObject dataHome = null; 069 070 protected String quoteStringCache_ = ""; 071 072 /** 073 * Default constructor for the Yahoo! Finance webpage scraper. 074 * <a href="https://finance.google.com">https://finance.google.com</a> is a site requiring login; this class wraps the login and 075 * credentials away and just focusses on the scraping of data from the restricted pages.<br><br> 076 * 077 * The constructor ONLY gets the class fields and properties setup to go, BUT does no web page access; 078 * that is left up to the doLogin and methods. 079 **/ 080 public GoogleFinanceScraper( ) 081 { 082 super(); 083 084 alreadyLoggedIn_ = true; 085 loadProperties(); 086 087 reqProps.put("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); 088 reqProps.put("Accept-Encoding ","gzip, deflate, br"); 089 reqProps.put("Accept-Language ","en-US,en;q=0.5"); 090 reqProps.put("Connection","keep-alive"); 091 reqProps.put("Cookie","_ga=GA1.2-2.2126383985.1485192339; SC=RV=:ED=ca; _ga=GA1.3-2.2126383985.1485192339; _gid=GA1.3-2.1169777638.1511223678; NID=117=XCFBclyBN8yMrHodqAHGAnzZqTHHgdgIC7ZMLZEstpmmhvehgkI0QeHLVkvWLG0uySKvItWNuKphGmBAJe4QzCD6MBGfgLclSeW27xySZ5L_M-iSNTfeVfQHYCkl0tqP15yuhEvq7f24oyMTTyVT_QN9B3y9o8EN1Anhb9JZ; OGPC=5061451-2:5061821-3:; 1P_JAR=2017-11-21-2; OGP=-5061451:-5061821:; __gads=ID=71d1c22d471495fa:T=1508459738:S=ALNI_MbCde9o7niI4fDhlWp2dJCbeAAdNQ; S=quotestreamer=PY9m_pUIUhUQlj-rEAMVepsfJzJoAXRV"); 092 reqProps.put("Upgrade-Insecure-Requests","1"); 093 reqProps.put("Host","finance.google.ca"); 094 095 setLoginUrl(gLoginUrl_pshort); 096 setLoginFormID(gLoginFormElement); 097 setUsernameFormElementName(gUserLoginElement); 098 setPasswordFormElementName(gPasswordElement); 099 setUsername(gUsername); 100 setPassword(gPassword); 101 setScrapePageUrl(gScrapePageUrl); 102 setScrapeStart(gScrapeQuoteStart); 103 setScrapeEnd(gScrapeQuoteEnd); 104 setRequestProps(reqProps); 105 } 106 107 108 /** 109 * Load the private properties from an external/private file. 110 * Site login credentials are NOT kept in this class; they are in a properties file called .credential in the 'user.home' directory. 111 * <br>The properites that should be in this file:<ul><li>username</li><li>password</li></ul>Maybe the request properties for the website could go in here too. 112 * 113 * @return truee if successful, false if could not load from file 114 **/ 115 private boolean loadProperties() 116 { 117 boolean retVal = false; 118 try 119 { 120 //get user/pass from ~/.credential 121 Properties gProps = new Properties(); 122 gProps.load(new java.io.FileReader(googleFinanceFilename_)); 123 gUsername = gProps.getProperty("username"); 124 gPassword = gProps.getProperty("password"); 125 retVal = true; 126 } 127 catch (Exception ex) 128 { 129 // use defaults or set before use 130 System.out.println("ERROR : did not read props from :"+googleFinanceFilename_); 131 } 132 133 return retVal; 134 } 135 136 137 /** 138 * Not needed because this is a open webpage. 139 **/ 140 @Override 141 public boolean doLogin() 142 { 143 boolean retVal = alreadyLoggedIn_; 144 reqProps.put("Content-Type","application/x-www-form-urlencoded"); 145 146 if(!alreadyLoggedIn_) 147 if(!"".equals(getUsername()) && !"".equals(getPassword())) 148 retVal = super.doLogin(); 149 else 150 System.out.println("ERROR reading username and password"); 151 152 return retVal; 153 } 154 155 156 /** Get the latest stock QUOTE using old website. 157 * 158 * @param stockSymbol is the Symbol of the stock to lookup 159 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 160 * @return the quote OR 0.0 if not found 161 **/ 162 private double getQuoteOld(String stockSymbol, String marketSymbol) 163 { 164 165 double retVal = 0.0; 166 String currPriceStr = null; 167 String result = getQuoteString(stockSymbol, marketSymbol); 168 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 169 //Looking for 170 // <span class="pr"> 171 // <span id="ref_630737340410842_l"> 172 // 3.72</span> 173 // </span> 174 try 175 { 176 int offset = "class=\"pr\">".length(); 177 int s= result.indexOf("class=\"pr\">"); 178 if (s==-1) 179 { 180 s = result.indexOf("class=pr>"); 181 currPriceStr = result.substring( s); 182 currPriceStr = currPriceStr.substring( currPriceStr.indexOf('>')+1); // should be from 3.72</span 183 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('<')); 184 retVal = Double.parseDouble(currPriceStr); 185 } 186 else 187 { 188 currPriceStr = result.substring( s); 189 currPriceStr = currPriceStr.substring( currPriceStr.indexOf('>', offset)+1); // should be from 3.72</span 190 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('<')); 191 retVal = Double.parseDouble(currPriceStr); 192 } 193 } 194 catch(java.lang.StringIndexOutOfBoundsException oobEx) 195 { 196 System.out.println("ERROR: get quote parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 197 oobEx.printStackTrace(); 198 System.out.println(currPriceStr); 199 } 200 return retVal; 201 } 202 203 204 /** Get the latest stock QUOTE from new Website Circa 2018. 205 * 206 * @param stockSymbol is the Symbol of the stock to lookup 207 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 208 * @return the quote OR 0.0 if not found 209 **/ 210 private double getQuoteNew(String stockSymbol, String marketSymbol) 211 { 212 213 double retVal = 0.0; 214 String currPriceStr = null; 215 String result = getQuoteString(stockSymbol, marketSymbol); 216 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 217 try 218 { 219 220 String str = "<div><span><span jsl=\"$t t-cLlm2e1WimE;$x 0;\""; 221 String str2 = "<span class=\"IsqQVc NprOob inM7_hGITiio-zJFzKq8ukm8\">"; // needed if there is a 2nd option 222 String str3 = "><span class=\""; 223 int offset = str.length(); 224 int s= result.indexOf(str); 225 if (s==-1) 226 { 227 offset = str2.length(); 228 s = result.indexOf(str2); 229 if (s!=-1) 230 { 231 currPriceStr = result.substring( s+offset); 232 int offset2 = str3.length(); 233 int s2= currPriceStr.indexOf(str3); 234 currPriceStr = currPriceStr.substring( s2+offset2); 235 int s3= currPriceStr.indexOf("\">"); 236 currPriceStr = currPriceStr.substring( s3+2); 237 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("</span>")); 238 retVal = Double.parseDouble(currPriceStr.replace(",","")); 239 } 240 } 241 else 242 { 243 currPriceStr = result.substring( s+offset); 244 int offset2 = str3.length(); 245 int s2= currPriceStr.indexOf(str3); 246 currPriceStr = currPriceStr.substring( s2+offset2); 247 int s3= currPriceStr.indexOf("\">"); 248 currPriceStr = currPriceStr.substring( s3+2); 249 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("</span>")); 250 retVal = Double.parseDouble(currPriceStr.replace(",","")); 251 } 252 } 253 catch(java.lang.StringIndexOutOfBoundsException oobEx) 254 { 255 System.out.println("ERROR: get quote parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 256 oobEx.printStackTrace(); 257 System.out.println(currPriceStr); 258 } 259 return retVal; 260 } 261 262 263 /** Get the latest stock QUOTE . 264 * 265 * @param stockSymbol is the Symbol of the stock to lookup 266 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 267 * @return the quote OR 0.0 if not found 268 **/ 269 public double getQuote(String stockSymbol, String marketSymbol) 270 { 271 return getQuoteNew(stockSymbol, marketSymbol); 272 } 273 274 275 public String getTodaysTSXChartAndTableHtmlStr() 276 { 277 return getTodaysTSXChartAndTableHtmlStr(true); 278 } 279 280 281 /** Get the days TSX Index chart and data . 282 * 283 * @return html containing todays TSX indices chart 284 **/ 285 public String getTodaysTSXChartAndTableHtmlStr(boolean includeTable) 286 { 287 String retVal = ""; 288 boolean success = false; 289 boolean doLogin = false; 290 setScrapePageUrl("https://finance.google.ca/finance"); 291 setScrapeStart("<div class=id-summary-chart>"); 292 setScrapeEnd("</div></div><div class=\"sfe-section clf\">"); 293 //setScrapeStart("BlahBlahBlah"); 294 //setScrapeEnd("BlahBlahBlah"); 295 296 HashMap <String, String> reqProps = null; 297 298 setDebugOut(); 299 300 if(doLogin) 301 { 302 if(!"".equals(getUsername()) && !"".equals(getPassword())) 303 { 304 success = doLogin(); 305 //System.out.println("\nLogin Response:\n"+postPageResponse_); 306 } 307 else 308 System.out.println("ERROR reading username and password"); 309 } 310 else 311 success = true; 312 313 if(success) 314 { 315 String result = doScrape(false); 316 retVal = result; 317 318 //System.out.println("Scraped Chart:\n"+retVal); 319 } 320 321 /* 322 //Looking for 323 // <span class="pr"> 324 // <span id="ref_630737340410842_l"> 325 // 3.72</span> 326 // </span> 327 try 328 { 329 int offset = "Vol / Avg.</td>".length(); 330 int s= result.indexOf("Vol / Avg.</td>"); 331 if (s==-1) 332 { 333 s = result.indexOf("class=pr>"); 334 if (s!=-1) 335 { 336 currPriceStr = result.substring( s); 337 currPriceStr = currPriceStr.substring( currPriceStr.indexOf('>')+1); // should be from 3.72</span 338 if(currPriceStr.indexOf('M')!=-1 && currPriceStr.indexOf('M')<8) 339 { 340 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('M')); 341 retVal = Double.parseDouble(currPriceStr.replace(",",""))*1000000.0; 342 } 343 else 344 { 345 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('/')); 346 retVal = Double.parseDouble(currPriceStr.replace(",","")); 347 } 348 } 349 } 350 else 351 { 352 currPriceStr = result.substring( s); 353 //System.out.println("\nDEBUG:"+currPriceStr); 354 currPriceStr = currPriceStr.substring( currPriceStr.indexOf('>', offset)+1); // should be from 3.72</span 355 //System.out.println("\nDEBUG:"+currPriceStr); 356 if(currPriceStr.indexOf('M')!=-1 && currPriceStr.indexOf('M')<8) 357 { 358 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('M')); 359 retVal = Double.parseDouble(currPriceStr.replace(",",""))*1000000.0; 360 } 361 else 362 { 363 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('/')); 364 retVal = Double.parseDouble(currPriceStr.replace(",","")); 365 } 366 } 367 } 368 catch(java.lang.StringIndexOutOfBoundsException oobEx) 369 { 370 System.out.println("ERROR: get volume parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 371 oobEx.printStackTrace(); 372 System.out.println(currPriceStr); 373 } 374 */ 375 return retVal; 376 } 377 378 379 /** Get the latest stock stat for the passed in valueName from the NEW GFinance pages Circa 2018. 380 * 381 * @param stockSymbol is the Symbol of the stock to lookup 382 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 383 * @param valueName is the stat to scrape - Open, Close, High, Low, 52-wk high, 52-wk low, Div yield 384 * @return the price OR -1 if not found; 385 **/ 386 private double scrapeValueNew(String stockSymbol, String marketSymbol, String valueName) 387 { 388 double retVal = -1.0; 389 String currPriceStr = null; 390 if ("Close".equalsIgnoreCase(valueName)) valueName = "Prev close"; 391 if ("Prev close".equalsIgnoreCase(valueName)) valueName = "Prev close"; 392 if ("Div".equalsIgnoreCase(valueName)) valueName = "Div yield"; 393 if ("Dividend".equalsIgnoreCase(valueName)) valueName = "Div yield"; 394 if ("Div yield".equalsIgnoreCase(valueName)) valueName = "Div yield"; 395 if(!"".equalsIgnoreCase(marketSymbol)) 396 { 397 retVal = 0.0; 398 String result = getQuoteString(stockSymbol, marketSymbol); 399 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 400 //Looking for 401 // <div class="ZSM8k"><table><tr> 402 // <td class="JgXcPd">Open</td><td class="iyjjgb">21.50</td></tr> 403 // <tr><td class="JgXcPd">High</td><td class="iyjjgb">21.93</td></tr> 404 // <tr><td class="JgXcPd">Low</td><td class="iyjjgb">20.65</td></tr> 405 // <tr><td class="JgXcPd">Mkt cap</td><td class="iyjjgb">567.04M</td></tr> 406 // <tr><td class="JgXcPd">P/E ratio</td><td class="iyjjgb">10.42</td></tr> 407 // </table></div> 408 // <div class="ZSM8k"><table><tr> 409 // <td class="JgXcPd">Div yield</td><td class="iyjjgb">1.94%</td></tr> 410 // <tr><td class="JgXcPd">Prev close</td><td class="iyjjgb">20.87</td></tr> 411 // <tr><td class="JgXcPd">52-wk high</td><td class="iyjjgb">26.11</td></tr> 412 // <tr><td class="JgXcPd">52-wk low</td><td class="iyjjgb">17.46</td></tr> 413 // </table></div> 414 try 415 { 416 String str = "<td class=\"JgXcPd\">"+valueName+"</td><td class=\"iyjjgb\">"; 417 String str2 = "<td class=JgXcPd>"+valueName+"</td><td class=iyjjgb>"; 418 int offset = str.length(); 419 int s= result.indexOf(str); 420 if (s==-1) 421 { 422 offset = str2.length(); 423 s = result.indexOf(str2); 424 if (s!=-1) 425 { 426 currPriceStr = result.substring( s+offset); 427 if(valueName == "Div yield") 428 { 429 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("%</")); 430 if(currPriceStr.startsWith("-")) currPriceStr = "0.0"; 431 } 432 else 433 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("</")); 434 retVal = Double.parseDouble(currPriceStr.replace(",","")); 435 } 436 } 437 else 438 { 439 currPriceStr = result.substring( s+offset); 440 if(valueName == "Div yield") 441 { 442 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("%</")); 443 if(currPriceStr.startsWith("-")) currPriceStr = "0.0"; 444 } 445 else 446 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("</")); 447 retVal = Double.parseDouble(currPriceStr.replace(",","")); 448 } 449 } 450 catch(java.lang.StringIndexOutOfBoundsException oobEx) 451 { 452 System.out.println("ERROR: scraping "+valueName+". See result html in file "+ "gQuote-"+stockSymbol+".txt"); 453 oobEx.printStackTrace(); 454 System.out.println(currPriceStr); 455 } 456 catch(java.lang.NumberFormatException mfEx) 457 { 458 System.out.println("ERROR: number Format error scraping "+valueName+". See result html in file "+ "gQuote-"+stockSymbol+".txt"); 459 mfEx.printStackTrace(); 460 System.out.println(currPriceStr); 461 } 462 } 463 return retVal; 464 } 465 466 467 /** Get the latest stock Open price from the OLD GFinance Pages . 468 * 469 * @param stockSymbol is the Symbol of the stock to lookup 470 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 471 * @return the price OR -1 if not found; 472 **/ 473 private double getOpenOld(String stockSymbol, String marketSymbol) 474 { 475 476 double retVal = -1.0; 477 String currPriceStr = null; 478 if(!"".equalsIgnoreCase(marketSymbol)) 479 { 480 retVal = 0.0; 481 String result = getQuoteString(stockSymbol, marketSymbol); 482 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 483 //Looking for 484 // <span class="pr"> 485 // <span id="ref_630737340410842_l"> 486 // 3.72</span> 487 // </span> 488 try 489 { 490 int offset = "Open</td><td class=\"val\">".length(); 491 int s= result.indexOf("Open</td><td class=\"val\">"); 492 if (s==-1) 493 { 494 offset = "Open</td><td class=val>".length(); 495 s = result.indexOf("Open</td><td class=val>"); 496 if (s!=-1) 497 { 498 currPriceStr = result.substring( s+offset); 499 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("</")); 500 retVal = Double.parseDouble(currPriceStr.replace(",","")); 501 } 502 } 503 else 504 { 505 currPriceStr = result.substring( s+offset); 506 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf("</")); 507 retVal = Double.parseDouble(currPriceStr.replace(",","")); 508 } 509 } 510 catch(java.lang.StringIndexOutOfBoundsException oobEx) 511 { 512 System.out.println("ERROR: get open parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 513 oobEx.printStackTrace(); 514 System.out.println(currPriceStr); 515 } 516 } 517 return retVal; 518 } 519 520 521 /** Get the latest stock Open price from the NEW GFinance pages Circa 2018. 522 * 523 * @param stockSymbol is the Symbol of the stock to lookup 524 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 525 * @return the price OR -1 if not found; 526 **/ 527 private double getOpenNew(String stockSymbol, String marketSymbol) 528 { 529 return scrapeValueNew(stockSymbol, marketSymbol, "Open"); 530 } 531 532 533 /** Get the latest stock Open price. 534 * 535 * @param stockSymbol is the Symbol of the stock to lookup 536 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 537 * @return the price OR -1 if not found; 538 **/ 539 public double getOpen(String stockSymbol, String marketSymbol) 540 { 541 return getOpenNew(stockSymbol, marketSymbol); 542 } 543 544 545 /** Get the latest stock Days High price from the Old GFinance pages before 2018. 546 * 547 * @param stockSymbol is the Symbol of the stock to lookup 548 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 549 * @return the price OR -1 if not found; 550 **/ 551 private double getDaysHighOld(String stockSymbol, String marketSymbol) 552 { 553 554 double retVal = -1.0; 555 String currLowStr = null; 556 String currHighStr = null; 557 if(!"".equalsIgnoreCase(marketSymbol)) 558 { 559 retVal = 0.0; 560 String result = getQuoteString(stockSymbol, marketSymbol); 561 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 562 //Looking for 563 // Range</td><td class="val"> 564 try 565 { 566 int offset = "Range</td><td class=\"val\">".length(); 567 int s= result.indexOf("Range</td><td class=\"val\">"); 568 if (s==-1) 569 { 570 offset = "Range</td><td class=val>".length(); 571 s = result.indexOf("Range</td><td class=val>"); 572 if (s!=-1) 573 { 574 currLowStr = result.substring( s+offset); 575 currHighStr = currLowStr.substring( currLowStr.indexOf(" - ")+3); 576 currHighStr = currHighStr.substring( 0, currHighStr.indexOf("</td>")); 577 currLowStr = currLowStr.substring( 0, currLowStr.indexOf(" - ")); 578 retVal = Double.parseDouble(currHighStr.replace(",","")); 579 } 580 } 581 else 582 { 583 currLowStr = result.substring( s+offset); 584 currHighStr = currLowStr.substring( currLowStr.indexOf(" - ")+3); 585 currHighStr = currHighStr.substring( 0, currHighStr.indexOf("</td>")); 586 currLowStr = currLowStr.substring( 0, currLowStr.indexOf(" - ")); 587 retVal = Double.parseDouble(currHighStr.replace(",","")); 588 } 589 } 590 catch(java.lang.StringIndexOutOfBoundsException oobEx) 591 { 592 System.out.println("ERROR: get days high parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 593 oobEx.printStackTrace(); 594 System.out.println(currHighStr); 595 } 596 } 597 return retVal; 598 } 599 600 601 /** Get the latest stock Days High price from the NEW GFinance pages Circa 2018. 602 * 603 * @param stockSymbol is the Symbol of the stock to lookup 604 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 605 * @return the price OR -1 if not found; 606 **/ 607 private double getDaysHighNew(String stockSymbol, String marketSymbol) 608 { 609 return scrapeValueNew(stockSymbol, marketSymbol, "High"); 610 } 611 612 613 /** Get the latest stock Days High price . 614 * 615 * @param stockSymbol is the Symbol of the stock to lookup 616 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 617 * @return the price OR -1 if not found; 618 **/ 619 public double getDaysHigh(String stockSymbol, String marketSymbol) 620 {return getDaysHighNew( stockSymbol, marketSymbol);} 621 622 623 /** Get the latest stock Days Low price . 624 * 625 * @param stockSymbol is the Symbol of the stock to lookup 626 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 627 * @return the price OR -1 if not found; 628 **/ 629 private double getDaysLowOld(String stockSymbol, String marketSymbol) 630 { 631 632 double retVal = -1.0; 633 String currLowStr = null; 634 String currHighStr = null; 635 if(!"".equalsIgnoreCase(marketSymbol)) 636 { 637 retVal = 0.0; 638 String result = getQuoteString(stockSymbol, marketSymbol); 639 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 640 //Looking for 641 // Range</td><td class="val"> 642 try 643 { 644 int offset = "Range</td><td class=\"val\">".length(); 645 int s= result.indexOf("Range</td><td class=\"val\">"); 646 if (s==-1) 647 { 648 offset = "Range</td><td class=val>".length(); 649 s = result.indexOf("Range</td><td class=val>"); 650 if (s!=-1) 651 { 652 currLowStr = result.substring( s+offset); 653 currHighStr = currLowStr.substring( currLowStr.indexOf(" - ")+3); 654 currHighStr = currHighStr.substring( 0, currHighStr.indexOf("</td>")); 655 currLowStr = currLowStr.substring( 0, currLowStr.indexOf(" - ")); 656 retVal = Double.parseDouble(currLowStr.replace(",","")); 657 } 658 } 659 else 660 { 661 currLowStr = result.substring( s+offset); 662 currHighStr = currLowStr.substring( currLowStr.indexOf(" - ")+3); 663 currHighStr = currHighStr.substring( 0, currHighStr.indexOf("</td>")); 664 currLowStr = currLowStr.substring( 0, currLowStr.indexOf(" - ")); 665 retVal = Double.parseDouble(currLowStr.replace(",","")); 666 } 667 } 668 catch(java.lang.StringIndexOutOfBoundsException oobEx) 669 { 670 System.out.println("ERROR: get days high parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 671 oobEx.printStackTrace(); 672 System.out.println(currLowStr); 673 } 674 } 675 return retVal; 676 } 677 678 679 /** Get the latest stock Days Low price from the NEW GFinance pages Circa 2018. 680 * 681 * @param stockSymbol is the Symbol of the stock to lookup 682 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 683 * @return the price OR -1 if not found; 684 **/ 685 private double getDaysLowNew(String stockSymbol, String marketSymbol) 686 { 687 return scrapeValueNew(stockSymbol, marketSymbol, "Low"); 688 } 689 690 691 /** Get the latest stock Days Low price . 692 * 693 * @param stockSymbol is the Symbol of the stock to lookup 694 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 695 * @return the price OR -1 if not found; 696 **/ 697 public double getDaysLow(String stockSymbol, String marketSymbol) 698 {return getDaysLowNew( stockSymbol, marketSymbol);} 699 700 701 /** Get the latest stock Dividend Yield % . 702 * Latest dividend/dividend yield<br><br> 703 * Latest dividend is dividend per share paid to shareholders in the most recent quarter.<br> 704 * Dividend yield is the value of the latest dividend, multiplied by the number of times dividends are typically paid per year, divided by the stock price. 705 * 706 * @param stockSymbol is the Symbol of the stock to lookup 707 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 708 * @return the % OR -1 if not found; 709 **/ 710 private double getLatestDividendOld(String stockSymbol, String marketSymbol) 711 { 712 713 double retVal = -1.0; 714 String findStr = "Div/yield</td><td class=\"val\">"; 715 String findStr2 = "Div/yield</td><td class=val>"; 716 String dividendStr = null; 717 String divYieldStr = null; 718 if(!"".equalsIgnoreCase(marketSymbol)) 719 { 720 retVal = 0.0; 721 String result = getQuoteString(stockSymbol, marketSymbol); 722 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 723 //Looking for 724 // Range</td><td class="val"> 725 try 726 { 727 int offset = findStr.length(); 728 int s= result.indexOf(findStr); 729 if (s==-1) 730 { 731 offset = findStr2.length(); 732 s = result.indexOf(findStr2); 733 if (s!=-1) 734 { 735 dividendStr = result.substring( s+offset); 736 if(!dividendStr.startsWith("&")) 737 { 738 divYieldStr = dividendStr.substring( dividendStr.indexOf("/")+1); 739 divYieldStr = divYieldStr.substring( 0, divYieldStr.indexOf("</td>")); 740 dividendStr = dividendStr.substring( 0, dividendStr.indexOf("/")); 741 retVal = Double.parseDouble(dividendStr.replace(",","")); 742 } 743 } 744 } 745 else 746 { 747 dividendStr = result.substring( s+offset); 748 if(!dividendStr.startsWith("&")) 749 { 750 divYieldStr = dividendStr.substring( dividendStr.indexOf("/")+1); 751 divYieldStr = divYieldStr.substring( 0, divYieldStr.indexOf("</td>")); 752 dividendStr = dividendStr.substring( 0, dividendStr.indexOf("/")); 753 retVal = Double.parseDouble(dividendStr.replace(",","")); 754 } 755 } 756 } 757 catch(java.lang.StringIndexOutOfBoundsException oobEx) 758 { 759 System.out.println("ERROR: get dividend parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 760 oobEx.printStackTrace(); 761 System.out.println(dividendStr); 762 } 763 } 764 return retVal; 765 } 766 767 768 /** Get the latest stock Div Yield % from the NEW GFinance pages Circa 2018. 769 * 770 * @param stockSymbol is the Symbol of the stock to lookup 771 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 772 * @return the div % OR -1 if not found; 773 **/ 774 private double getLatestDividendNew(String stockSymbol, String marketSymbol) 775 { 776 return scrapeValueNew(stockSymbol, marketSymbol, "Div yield"); 777 } 778 779 780 /** Get the latest stock Div Yield %. 781 * 782 * @param stockSymbol is the Symbol of the stock to lookup 783 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 784 * @return the div % OR -1 if not found; 785 **/ 786 public double getLatestDividend(String stockSymbol, String marketSymbol) 787 {return getLatestDividendNew( stockSymbol, marketSymbol);} 788 789 790 /** Get the latest stock Volume . 791 * 792 * @param stockSymbol is the Symbol of the stock to lookup 793 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 794 * @return the most recent daily volume OR -1 if not found; 795 **/ 796 private double getVolumeOld(String stockSymbol, String marketSymbol) 797 { 798 799 double retVal = -1.0; 800 String currPriceStr = null; 801 if(!"MUTF_CA".equalsIgnoreCase(marketSymbol)) 802 { 803 retVal = 0.0; 804 String result = getQuoteString(stockSymbol, marketSymbol); 805 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 806 //Looking for 807 // <span class="pr"> 808 // <span id="ref_630737340410842_l"> 809 // 3.72</span> 810 // </span> 811 try 812 { 813 int offset = "Vol / Avg.</td>".length(); 814 int s= result.indexOf("Vol / Avg.</td>"); 815 if (s==-1) 816 { 817 s = result.indexOf("class=pr>"); 818 if (s!=-1) 819 { 820 currPriceStr = result.substring( s); 821 currPriceStr = currPriceStr.substring( currPriceStr.indexOf('>')+1); // should be from 3.72</span 822 if(currPriceStr.indexOf('M')!=-1 && currPriceStr.indexOf('M')<8) 823 { 824 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('M')); 825 retVal = Double.parseDouble(currPriceStr.replace(",",""))*1000000.0; 826 } 827 else 828 { 829 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('/')); 830 retVal = Double.parseDouble(currPriceStr.replace(",","")); 831 } 832 } 833 } 834 else 835 { 836 currPriceStr = result.substring( s); 837 //System.out.println("\nDEBUG:"+currPriceStr); 838 currPriceStr = currPriceStr.substring( currPriceStr.indexOf('>', offset)+1); // should be from 3.72</span 839 //System.out.println("\nDEBUG:"+currPriceStr); 840 if(currPriceStr.indexOf('M')!=-1 && currPriceStr.indexOf('M')<8) 841 { 842 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('M')); 843 retVal = Double.parseDouble(currPriceStr.replace(",",""))*1000000.0; 844 } 845 else 846 { 847 currPriceStr = currPriceStr.substring( 0, currPriceStr.indexOf('/')); 848 retVal = Double.parseDouble(currPriceStr.replace(",","")); 849 } 850 } 851 } 852 catch(java.lang.StringIndexOutOfBoundsException oobEx) 853 { 854 System.out.println("ERROR: get volume parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 855 oobEx.printStackTrace(); 856 System.out.println(currPriceStr); 857 } 858 } 859 return retVal; 860 } 861 862 863 /** Get the latest stock Volume from the NEW GFinance pages Circa 2018 - IF IT EXISTED. 864 * 865 * @param stockSymbol is the Symbol of the stock to lookup 866 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 867 * @return the Volume from most resent day -1 if not found; 868 **/ 869 private double getVolumeNew(String stockSymbol, String marketSymbol) 870 { 871 return -1.0; // does not exist in new Google... scrapeValueNew(stockSymbol, marketSymbol, "Div yield"); 872 } 873 874 875 /** Get the latest stock Volume. 876 * 877 * @param stockSymbol is the Symbol of the stock to lookup 878 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 879 * @return the div % OR -1 if not found; 880 **/ 881 public double getVolume(String stockSymbol, String marketSymbol) 882 {return getVolumeNew( stockSymbol, marketSymbol);} 883 884 885 /** Get the latest stock Dividend Yield. 886 * Latest dividend/dividend yield<br><br> 887 * Latest dividend is dividend per share paid to shareholders in the most recent quarter.<br> 888 * Dividend yield is the value of the latest dividend, multiplied by the number of times dividends are typically paid per year, divided by the stock price. 889 * 890 * @param stockSymbol is the Symbol of the stock to lookup 891 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 892 * @return the price OR -1 if not found; 893 **/ 894 public double getDividendYieldOld(String stockSymbol, String marketSymbol) 895 { 896 897 double retVal = -1.0; 898 String findStr = "Div/yield</td><td class=\"val\">"; 899 String findStr2 = "Div/yield</td><td class=val>"; 900 String dividendStr = null; 901 String divYieldStr = null; 902 if(!"".equalsIgnoreCase(marketSymbol)) 903 { 904 retVal = 0.0; 905 String result = getQuoteString(stockSymbol, marketSymbol); 906 writeStringToFile(result,"gQuote-"+stockSymbol+".txt"); 907 //Looking for 908 // Range</td><td class="val"> 909 try 910 { 911 int offset = findStr.length(); 912 int s= result.indexOf(findStr); 913 if (s==-1) 914 { 915 offset = findStr2.length(); 916 s = result.indexOf(findStr2); 917 if (s!=-1) 918 { 919 dividendStr = result.substring( s+offset); 920 if(!dividendStr.startsWith("&")) 921 { 922 divYieldStr = dividendStr.substring( dividendStr.indexOf("/")+1); 923 divYieldStr = divYieldStr.substring( 0, divYieldStr.indexOf("</td>")); 924 dividendStr = dividendStr.substring( 0, dividendStr.indexOf("/")); 925 retVal = Double.parseDouble(divYieldStr.replace(",","")); 926 } 927 } 928 } 929 else 930 { 931 dividendStr = result.substring( s+offset); 932 if(!dividendStr.startsWith("&")) 933 { 934 divYieldStr = dividendStr.substring( dividendStr.indexOf("/")+1); 935 divYieldStr = divYieldStr.substring( 0, divYieldStr.indexOf("</td>")); 936 dividendStr = dividendStr.substring( 0, dividendStr.indexOf("/")); 937 retVal = Double.parseDouble(divYieldStr.replace(",","")); 938 } 939 } 940 } 941 catch(java.lang.StringIndexOutOfBoundsException oobEx) 942 { 943 System.out.println("ERROR: get dividend Yield parse error. See result html in file "+ "gQuote-"+stockSymbol+".txt"); 944 oobEx.printStackTrace(); 945 System.out.println(divYieldStr); 946 } 947 } 948 return retVal; 949 } 950 951 952 /** 953 * Returns the URL string to the Google page for the specified stock. 954 **/ 955 public static String getStockPageUrlStr(String stockSymbol, String marketSymbol) 956 { 957 String retVal = ""; 958 retVal = gScrapeQuoteTokenizedUrl.replace(QUOTE_SYMBOL_TOKEN, stockSymbol); 959 retVal = retVal.replace(QUOTE_MARKET_TOKEN, googleMarketSymbol(marketSymbol)); 960 961 return retVal; 962 } 963 964 965 /** Requests, scrapes and creates the stock QUOTE data string for the given stock symbol for today. 966 * this data gets used as the data load into the InvestmentTrackerQuery database.<br> 967 * It returns a multi-line result-set string with the first line as the column headings and the remaining lies of data<br> 968 * open, daysLow, daysHigh, previousClose, daysVolume, dividendPerShare, dividendPayDate<br> 969 * 90.01, 89.13, 90.01, 90.34, 386147, 4.18, "10/27/2017" 970 * 971 * @param stockSymbol is the Symbol of the stock to lookup 972 * @param marketSymbol is the stock market symbol that Google uses in the quoteUrl 973 * @return the delimited resultSet OR '' if not found; the resultset is a comma delimited open, daysLow, daysHigh, previousClose, daysVolume, dividendPerShare, dividendPayDate OR '' if not found 974 **/ 975 private String getQuoteString(String stockSymbol, String marketSymbol) { return getQuoteString(stockSymbol, marketSymbol, true);} 976 private String getQuoteString(String stockSymbol, String marketSymbol, boolean useCache) 977 { 978 String retVal = ""; 979 boolean success = false; 980 boolean useDataTool = false; 981 //NASDAQ:FCEL CVE:HIVE MUTF_CA:AGF201 TSE:LUN 982 983 if(useCache && stockSymbolCache_ == stockSymbol && stockSymbolCache_ == stockSymbol) 984 { 985 retVal = quoteStringCache_; 986 } 987 else 988 { 989 String tokenReplacedScrapeUrl = gScrapeQuoteTokenizedUrl.replace(QUOTE_SYMBOL_TOKEN,stockSymbol); 990 tokenReplacedScrapeUrl = tokenReplacedScrapeUrl.replace(QUOTE_MARKET_TOKEN,marketSymbol); 991 setScrapePageUrl(tokenReplacedScrapeUrl); 992 993 HashMap <String, String> reqProps = null; 994 995 if(holdings==null && !marketSymbol.equals("MUTF_CA")) // Google is banning my MUTF_CA queries 996 { 997 if(!"".equals(getUsername()) && !"".equals(getPassword())) 998 { 999 success = doLogin(); 1000 //System.out.println("\nLogin Response:\n"+postPageResponse_); 1001 } 1002 else 1003 System.out.println("ERROR reading username and password"); 1004 1005 if(success) 1006 { 1007 String result = doScrape(reqProps, true); // without any further trimming, only the start/end trimming 1008 retVal = result; 1009 } 1010 stockSymbolCache_ = stockSymbol; 1011 marketSymbolCache_ = marketSymbol; 1012 quoteStringCache_ = retVal; 1013 } 1014 } 1015 return retVal; 1016 } 1017 1018 1019 1020 /** Create the STOCK_DAILY data string for the given stock symbol for today. 1021 * this data gets used as the data load into the InvestmentTrackerQuery database.<br> 1022 * It returns a multi-line result-set string with the first line as the column headings and the remaining lies of data<br> 1023 * open, daysLow, daysHigh, close, previousClose, daysVolume, date, dividendPerShare, dividendPayDate<br> 1024 * 90.01, 89.13, 90.01, 90.00, 90.34, 386147, "11/08/2017", 4.18, "10/27/2017" 1025 * 1026 * @param stockSymbol is the Symbol of the stock to lookup * 1027 * @return the delimited resultSet OR '' if not found; the resultset is a comma delimited open, daysLow, daysHigh, previousClose, daysVolume, dividendPerShare, dividendPayDate OR '' if not found 1028 **/ 1029 public String getStockDailyString(String stockSymbol) 1030 { 1031 String retVal = ""; 1032 boolean success = false; 1033 JsonObject dataHome = null; 1034 if(holdings==null) 1035 { 1036 if(!"".equals(getUsername()) && !"".equals(getPassword())) 1037 success = doLogin(); 1038 else 1039 System.out.println("ERROR reading username and password"); 1040 1041 if(success) 1042 { 1043 // scape the summary first to get a list of holdings 1044 String result = doScrape(); 1045 JsonObject jsO = toJsonObject(result); 1046 JsonArray jsA = jsO.getJsonArray("Data"); 1047 dataHome = jsA.getJsonObject(0); 1048 holdings = dataHome.getJsonArray("Holdings"); 1049 } 1050 } 1051 else 1052 System.out.println("Already Logged in, using existing holdings["+holdings.size()+"]"); 1053 1054 if(dataHome!=null && holdings!=null) 1055 { 1056 retVal+="\"Open\""; 1057 retVal+=COL_DELIM; 1058 retVal+="\"daysLow\""; 1059 retVal+=COL_DELIM; 1060 retVal+="\"daysHigh\""; 1061 retVal+=COL_DELIM; 1062 retVal+="\"close\""; 1063 retVal+=COL_DELIM; 1064 retVal+="\"previousClose\""; 1065 retVal+=COL_DELIM; 1066 retVal+="\"date\""; 1067 retVal+=COL_DELIM; 1068 retVal+="\"dividendPerShare\""; 1069 retVal+=COL_DELIM; 1070 retVal+="\"dividendPayDate\""; 1071 retVal+="\n"; 1072 1073 setScrapeStart(gScrapeQuoteStart); 1074 setScrapeEnd(gScrapeQuoteEnd); 1075 String currSymbol = ""; 1076 String currPrefix = ""; 1077 JsonObject stock =null; 1078 1079 for(int i=0; i<holdings.size();i++) 1080 { 1081 stock = holdings.getJsonObject(i); 1082 if(stock!=null) 1083 { 1084 currSymbol = stock.getJsonString("Symbol").toString().substring(1,stock.getJsonString("Symbol").toString().length()-1); 1085 currPrefix = stock.getJsonString("CountryPrefix").toString().substring(1,stock.getJsonString("CountryPrefix").toString().length()-1); 1086 1087 if(("\""+stockSymbol+"\"").equalsIgnoreCase(currSymbol.toString())) 1088 { 1089 1090 //get the daily quote for each individual holding 1091 setScrapePageUrl(gScrapeQuoteTokenizedUrl.replace(QUOTE_SYMBOL_TOKEN,currPrefix+currSymbol)); 1092 String result = doScrape(); 1093 System.out.println(" DEBUG Stock Quote Result \n----------------------------\n"); 1094 System.out.println(result); 1095 1096 /* 1097 JsonObject jsO = toJsonObject(result); 1098 JsonString currSymbol = stock.getJsonString("Symbol"); 1099 if(("\""+stockSymbol+"\"").equalsIgnoreCase(currSymbol.toString())) 1100 { 1101 JsonNumber open, daysLow, daysHigh, previousClose, daysVolume, dividendPerShare; 1102 String dividendPayDate; 1103 //open=stock.getJsonString("Price"); 1104 retVal+="\""+dataHome.getString("FriendlyName")+"\""; 1105 retVal+=COL_DELIM; 1106 retVal+=""+dataHome.getJsonString("Currency"); 1107 retVal+=COL_DELIM; 1108 1109 } 1110 */ 1111 i=holdings.size(); 1112 } 1113 } 1114 } 1115 } 1116 1117 return retVal; 1118 } 1119 1120 1121 public static String googleMarketSymbol(String mkt) 1122 { 1123 String retVal = mkt; 1124 1125 if("TSXV".equalsIgnoreCase(mkt)) retVal = "CVE"; 1126 else if("TSX".equalsIgnoreCase(mkt)) retVal = "TSE"; 1127 else if("MUT".equalsIgnoreCase(mkt)) retVal = "MUTF_CA"; 1128 else if("MUTF".equalsIgnoreCase(mkt)) retVal = "MUTF_CA"; 1129 1130 return retVal; 1131 } 1132 1133 1134 /** Create the Portfolio Summary data string for the CR account for today. 1135 * It returns a multi-line result-set string with the first line as the column headings and the remaining lies of data<br> 1136 * FriendlyName, Currency, BookValue, TradeCash, MarketValue, EquityValue, UnrealizedGainLoss, UnrealizedGainLossPercent, numHoldings, date<br> 1137 * "#2V9669V6 - TFSA", 50.5, 10.1, 66.4, 15.9, 8, "10/27/2017" 1138 * 1139 * @return the delimited resultSet OR '' if not found; the resultset is a comma delimited stockName, symbol, CRQuerySymbol, numShares, currentPrice, currency, bookValue, marketValue, gain, gainPercent, currentDate 1140 **/ 1141 public String getPortfolioSummaryString() 1142 { 1143 String retVal = ""; 1144 boolean success = false; 1145 JsonObject dataHome = null; 1146 if(holdings==null) 1147 { 1148 if(!"".equals(getUsername()) && !"".equals(getPassword())) 1149 success = doLogin(); 1150 else 1151 System.out.println("ERROR reading username and password"); 1152 1153 if(success) 1154 { 1155 String result = doScrape(); 1156 JsonObject jsO = toJsonObject(result); 1157 JsonArray jsA = jsO.getJsonArray("Data"); 1158 dataHome = jsA.getJsonObject(0); 1159 //holdings = dataHome.getJsonArray("Holdings"); 1160 } 1161 } 1162 else 1163 System.out.println("Already Logged in, using existing holdings["+holdings.size()+"]"); 1164 1165 if(dataHome!=null) // && holdings!=null) 1166 { 1167 retVal+="\"friendlyName\""; 1168 retVal+=COL_DELIM; 1169 retVal+="\"Currency\""; 1170 retVal+=COL_DELIM; 1171 retVal+="\"BookValue\""; 1172 retVal+=COL_DELIM; 1173 retVal+="\"TradeCash\""; 1174 retVal+=COL_DELIM; 1175 retVal+="\"MarketValue\""; 1176 retVal+=COL_DELIM; 1177 retVal+="\"EquityValue\""; 1178 retVal+=COL_DELIM; 1179 retVal+="\"UnrealizedGainLoss\""; 1180 retVal+=COL_DELIM; 1181 retVal+="\"UnrealizedGainLossPercent\""; 1182 retVal+=COL_DELIM; 1183 retVal+="\"NumberOfHoldings\""; 1184 retVal+=COL_DELIM; 1185 retVal+="\"date\""; 1186 retVal+="\n"; 1187 1188 retVal+="\""+dataHome.getString("FriendlyName")+"\""; 1189 retVal+=COL_DELIM; 1190 retVal+=""+dataHome.getJsonString("Currency"); 1191 retVal+=COL_DELIM; 1192 retVal+=""+dataHome.getJsonNumber("BookValue"); 1193 retVal+=COL_DELIM; 1194 retVal+=""+dataHome.getJsonNumber("TradeCash"); 1195 retVal+=COL_DELIM; 1196 retVal+=""+dataHome.getJsonNumber("MarketValue"); 1197 retVal+=COL_DELIM; 1198 retVal+=""+dataHome.getJsonNumber("EquityValue"); 1199 retVal+=COL_DELIM; 1200 retVal+=""+dataHome.getJsonNumber("UnrealizedGainLoss"); 1201 retVal+=COL_DELIM; 1202 retVal+=""+dataHome.getJsonNumber("UnrealizedGainLossPercent"); 1203 retVal+=COL_DELIM; 1204 retVal+=""+(holdings!=null?holdings.size():"0"); 1205 retVal+=COL_DELIM; 1206 retVal+="\""+dateStr_+"\""; 1207 1208 /* 1209 for(int i=0; i<holdings.size();i++) 1210 { 1211 JsonObject stock = holdings.getJsonObject(i); 1212 if(stock!=null) 1213 { 1214 String currSymbol = stock.getJsonString("Symbol").toString().substring(1,stock.getJsonString("Symbol").toString().length()-1); 1215 System.out.println(currSymbol+" : "+stock.getJsonNumber("Quantity").toString()+" @ "+ 1216 stock.getJsonString("Price").toString().substring(1,stock.getJsonString("Price").toString().length()-1)+ 1217 " = "+stock.getJsonNumber("MarketValue").toString()); 1218 } 1219 } 1220 */ 1221 } 1222 1223 return retVal; 1224 } 1225 1226 1227 1228 /** Create the Portfolio data string for the CR account for today. 1229 * It returns a multi-line result-set string with the first line as the column headings and the remaining lies of data<br> 1230 * stockName, symbol, CRQuerySymbol, numShares, currentPrice, currency, bookValue, marketValue, gain, gainPercent, currentDate<br> 1231 * Hive Tech, HIVE, HIVE, 500, 43.4, CA, 1502.40, 2900.84, 1397.44, 83.4, "10/27/2017" 1232 * 1233 * @return the delimited resultSet OR '' if not found; the resultset is a comma delimited stockName, symbol, CRQuerySymbol, numShares, currentPrice, currency, bookValue, marketValue, gain, gainPercent, currentDate 1234 **/ 1235 public String getPortfolioString(){return getPortfolioString(true);} 1236 1237 1238 1239 /** Create the Portfolio data string for the CR account for today. 1240 * It returns a multi-line result-set string with the first line as the column headings and the remaining lies of data<br> 1241 * stockName, symbol, CRExchangeSymbol, numShares, currentPrice, currency, bookValue, marketValue, gain, gainPercent, currentDate<br> 1242 * Hive Tech, HIVE, V, 500, 43.4, CA, 1502.40, 2900.84, 1397.44, 83.4, 10-27-2017 1243 * 1244 * @param resultSetOnly true to return only the default delimited resultSet OR false to send a longer more readable string 1245 * @return the delimited resultSet OR '' if not found; the resultset is a comma delimited stockName, symbol, CRQuerySymbol, numShares, currentPrice, currency, bookValue, marketValue, gain, gainPercent, currentDate 1246 **/ 1247 public String getPortfolioString(boolean resultSetOnly) 1248 { 1249 String retVal = "stockName"+COL_DELIM+"symbol"+COL_DELIM+"CRQuerySymbol"+COL_DELIM+"numShares"+COL_DELIM+"currentPrice"+COL_DELIM+ 1250 "currency"+COL_DELIM+"bookValue"+COL_DELIM+"marketValue"+COL_DELIM+"gain"+COL_DELIM+"gainPercent"+COL_DELIM+"currentDate\n"; 1251 if(!resultSetOnly) retVal = "\n\n---------------------------------\n Portfolio Summary\n---------------------------------\n"; 1252 1253 DecimalFormat dfp = new DecimalFormat( "##0" ); 1254 DecimalFormat dfe = new DecimalFormat( "##0.000" ); 1255 boolean success = false; 1256 1257 if(holdings==null) 1258 { 1259 if(!"".equals(getUsername()) && !"".equals(getPassword())) 1260 success = doLogin(); 1261 else 1262 System.out.println("ERROR reading username and password"); 1263 1264 if(success) 1265 { 1266 String result = doScrape(); 1267 JsonObject jsO = toJsonObject(result); 1268 JsonArray jsA = jsO.getJsonArray("Data"); 1269 dataHome = jsA.getJsonObject(0); 1270 holdings = dataHome.getJsonArray("Holdings"); 1271 } 1272 } 1273 else 1274 System.out.println("Already Logged in, using existing holdings["+holdings.size()+"]"); 1275 1276 if(dataHome!=null && holdings!=null) 1277 { 1278 if(!resultSetOnly) 1279 { 1280 String friendlyName = dataHome.getString("FriendlyName"); 1281 1282 retVal+=" FriendlyName : "+friendlyName; 1283 retVal+="\n"; 1284 retVal+=" MarketValue : "+dataHome.getJsonNumber("MarketValue"); 1285 retVal+="\n"; 1286 retVal+=" TradeCash : "+dataHome.getJsonNumber("TradeCash"); 1287 retVal+="\n"; 1288 retVal+=" BookValue : "+dataHome.getJsonNumber("BookValue"); 1289 retVal+="\n"; 1290 retVal+=" ------------------ ------------------------"; 1291 retVal+="\n"; 1292 retVal+=" UnrealizedGainLoss : "+dataHome.getJsonNumber("UnrealizedGainLoss"); 1293 retVal+="\n"; 1294 retVal+=" UnrealizedGain% : "+ 1295 dfe.format(dataHome.getJsonNumber("UnrealizedGainLossPercent").doubleValue()*100.0); 1296 retVal+="\n"; 1297 retVal+="\n Holdings Summary\n - - - - - - - - - - - - -\n"; 1298 } 1299 1300 String currSymbol = ""; 1301 String priceStr = ""; 1302 String currencySymbol = ""; 1303 JsonObject stock = null; 1304 for(int i=0; i<holdings.size();i++) 1305 { 1306 stock = holdings.getJsonObject(i); 1307 if(stock!=null) 1308 { 1309 currSymbol = stock.getJsonString("Symbol").toString().substring(1,stock.getJsonString("Symbol").toString().length()-1); 1310 if(!resultSetOnly) 1311 { 1312 retVal+=" "+currSymbol+" : "+stock.getJsonNumber("Quantity").toString()+" @ "+ 1313 stock.getJsonString("Price").toString().substring(1,stock.getJsonString("Price").toString().length()-1)+ 1314 " = "+stock.getJsonNumber("MarketValue").toString(); 1315 retVal+="\n"; 1316 } 1317 else 1318 { 1319 if(right(stock.getJsonString("Price").toString() ,2).equals("U\"")) 1320 { 1321 priceStr = stock.getJsonString("Price").toString().substring(1,stock.getJsonString("Price").toString().length()-2); 1322 currencySymbol = "US$"; 1323 } 1324 else 1325 { 1326 priceStr = stock.getJsonString("Price").toString().substring(1,stock.getJsonString("Price").toString().length()-1); 1327 currencySymbol = "C$"; 1328 } 1329 //stockName, symbol, CRExchangeSymbol, numShares, currentPrice, currency, bookValue, marketValue, gain, gainPercent, currentDate 1330 retVal+=stock.getString("SymbolDescription").toString()+COL_DELIM+ 1331 currSymbol+COL_DELIM+ 1332 stock.getString("Exchange").toString()+COL_DELIM+ 1333 stock.getJsonNumber("Quantity").toString()+COL_DELIM+ 1334 priceStr+COL_DELIM+ 1335 currencySymbol+COL_DELIM+ 1336 stock.getJsonNumber("BookValue").toString()+COL_DELIM+ 1337 stock.getJsonNumber("MarketValue").toString()+COL_DELIM+ 1338 stock.getJsonNumber("UnrealizedGainLoss").toString()+COL_DELIM+ 1339 dfe.format(stock.getJsonNumber("UnrealizedGainLossPercent").doubleValue()*100.0)+COL_DELIM+ 1340 dateStr_; 1341 retVal+="\n"; 1342 } 1343 } 1344 } 1345 if(!resultSetOnly) retVal+="\n---------------------------\nHoldings Details:\n---------------------------\n"+ 1346 prettyJson(holdings.toString()); 1347 } 1348 1349 return retVal; 1350 } 1351 1352 1353 public String right(String value, int length) 1354 { 1355 // To get right characters from a string, change the begin index. 1356 return value.substring(value.length() - length); 1357 } 1358 1359 1360 /** Test method to do whatever tests I want. **/ 1361 @Override 1362 protected void test(String[] args) 1363 { 1364 super.debugOut_=true; 1365 debugOut_=true; 1366 1367 //NASDAQ:FCEL CVE:HIVE MUTF_CA:AGF201 TSE:LUN 1368 if(true) 1369 { 1370 String symbol = "AGF201"; 1371 String market = "MUTF_CA"; 1372 double result = getQuote(symbol,market); 1373 System.out.println("Test Quote For "+symbol+"="+result); 1374 //writeStringToFile(result,"gQuote-"+symbol+".txt"); 1375 } 1376 else if(false) 1377 { 1378 System.out.println("..."); 1379 try 1380 { 1381 1382 } 1383 catch (Exception ex) 1384 { 1385 System.out.println("NO GO..."); 1386 ex.printStackTrace(); 1387 } 1388 } 1389 } 1390 1391 1392 public static void main(String[] args) 1393 { 1394 GoogleFinanceScraper instance = new GoogleFinanceScraper(); 1395 1396 if(args.length>0 && args[0].toLowerCase().equals("-t")) 1397 instance.test(args); 1398 else if(args.length>1 && args[0].equalsIgnoreCase("quote")) 1399 { 1400 System.out.println("\n\nQuote For market:symbol "+args[1]+":"+args[2]+" = "+instance.getQuote(args[2],args[1])); 1401 System.out.println("Volume For market:symbol "+args[1]+":"+args[2]+" = "+instance.getVolume(args[2],args[1])); 1402 System.out.println("Open Price For market:symbol "+args[1]+":"+args[2]+" = "+instance.getOpen(args[2],args[1])); 1403 System.out.println("Days LOW Price For market:symbol "+args[1]+":"+args[2]+" = "+instance.getDaysLow(args[2],args[1])); 1404 System.out.println("Days High Price For market:symbol "+args[1]+":"+args[2]+" = "+instance.getDaysHigh(args[2],args[1])); 1405 System.out.println("Dividend Yield % For market:symbol "+args[1]+":"+args[2]+" = "+instance.getLatestDividend(args[2],args[1])); 1406 } 1407 else 1408 { 1409 boolean success = false; 1410 if(!"".equals(instance.getUsername()) && !"".equals(instance.getPassword())) 1411 success = instance.doLogin(); 1412 else 1413 System.out.println("ERROR reading username and password"); 1414 1415 if(success) 1416 { 1417 String result = instance.doScrape(); 1418 System.out.println("Saving Scraped page results to file: "+"gPageContent-"+instance.dateStr_+".json"); 1419 writeStringToFile(prettyJson(result),"gPageContent-"+instance.dateStr_+".json"); 1420 1421 System.out.println(instance.getPortfolioString()); 1422 } 1423 } 1424 } 1425}