Skip to content

Commit

Permalink
Added daily high/low to trending display & new sandbox mode
Browse files Browse the repository at this point in the history
  • Loading branch information
frossm committed Apr 9, 2021
1 parent 1e294cc commit 311517d
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 31 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Note that if Quoter has been installed via a snap, `quoter -c` is all that is ne
|-r | Remove saved securities. If you'd like them back you'll need to re-save them |
|-i | Ignore saved queries for this execution. They will remain saved |
|-z | Disable colorized output|
|-b | Use the IEXCloud.io sandbox instead of the production environment. This is just needed during development. Please note that the sandbox requires a sandbox key, not the production key. This can be obtained via the IEXCloud dashboard and set with the `-c` command line switch |

#### Security Information
|Option|Description|
Expand Down Expand Up @@ -64,6 +65,8 @@ Example:

It's executed by giving Quoter the **`-t`** command line switch. If there are 5 symbols on the command line, it will trend them all.

The display will show you the last three months of data with the daily range and the close price.

Please note that this call to IEXCloud is weighted heavily and will use quite a few of your allowed monthly calls. However, given you get 500,000 calls per month, there is probably plenty if it's not massively over used.


Expand Down
Binary file modified graphics/ScreenShot-Trending.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified graphics/ScreenShot.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.fross</groupId>
<artifactId>quoter</artifactId>
<version>2.4.3</version>
<version>2.5.0</version>
<packaging>jar</packaging>

<name>quoter</name>
Expand Down
2 changes: 1 addition & 1 deletion snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: quoter
version: '2.4.3'
version: '2.5.0'
summary: Command line utility to pull stock and index quotes
description: |
Quote fetches stock quotes and index data from IEXCloud.IO.
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/fross/quoter/Help.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static void Display() {
Output.printColorln(Ansi.Color.WHITE, " -r Remove saved securities");
Output.printColorln(Ansi.Color.WHITE, " -i Ignore saved queries for this execution only");
Output.printColorln(Ansi.Color.WHITE, " -z Disable colorized output");
Output.printColorln(Ansi.Color.WHITE, " -b Use IEXCloud Sandbox instead of production (remember to set new key");

Output.printColorln(Ansi.Color.YELLOW, "\nSecurity Information:");
Output.printColorln(Ansi.Color.WHITE, " -d Display more detailed security information");
Expand Down
86 changes: 62 additions & 24 deletions src/main/java/org/fross/quoter/HistoricalQuotes.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,18 @@ public class HistoricalQuotes {
/**
* GetHistorical3M(): Return map of date/closePrice for 3 months
*
* In the value portion of the returned map: [0]=close price [1]=day high price [2]=day low price
*
* @return
*/
public static Map<String, Float> getHistoricalQuotes(String symb, String token) {
String QUOTEURLTEMPLATE = "https://cloud.iexapis.com/stable/stock/SYMBOLHERE/chart/3m?token=TOKENHERE";
public static Map<String, Float[]> getHistoricalQuotes(String symb, String token) {
String QUOTEURLTEMPLATE = Main.IEXCloudBaseURL + "/stable/stock/SYMBOLHERE/chart/3m?token=TOKENHERE";
String quoteURL = "";
String rawChartData = "";
Map<String, Float> resultMap = new TreeMap<String, Float>(); // TreeMaps are sorted

// Key is a date string
// Value is an array: [0]=close [1]=day high [2]=day low
Map<String, Float[]> resultMap = new TreeMap<String, Float[]>();

// Rewrite the template URL with the provided values
Output.debugPrint("Processing Trending for Symbol: '" + symb + "'");
Expand Down Expand Up @@ -85,7 +90,17 @@ public static Map<String, Float> getHistoricalQuotes(String symb, String token)
// In Gson, convert the JSON into a map
@SuppressWarnings("unchecked")
Map<String, Object> gsonMap = gson.fromJson(rawChartArray[i], Map.class);
resultMap.put(gsonMap.get("date").toString(), Float.parseFloat(gsonMap.get("close").toString()));

// Populate the array (which becomes resultMap value) with data fields from IEXCloud
Float tempArray[] = new Float[3];
tempArray[0] = Float.parseFloat(gsonMap.get("close").toString());
tempArray[1] = Float.parseFloat(gsonMap.get("high").toString());
tempArray[2] = Float.parseFloat(gsonMap.get("low").toString());

// Show the array in debug mode
Output.debugPrint(gsonMap.get("date").toString() + "\tClose:" + tempArray[0] + "\tHigh:" + tempArray[1] + "\tLow:" + tempArray[2]);

resultMap.put(gsonMap.get("date").toString(), tempArray);
}

} catch (Exception ex) {
Expand All @@ -96,34 +111,34 @@ public static Map<String, Float> getHistoricalQuotes(String symb, String token)
}

/**
* LargestMapValue(): Return largest float value of the provided map
* LargestMapValue(): Return largest daily high value of the provided map
*
* @param map
*/
public static Float largestMapValue(Map<String, Float> map) {
public static Float largestMapValue(Map<String, Float[]> map) {
Float largestValue = Float.MIN_VALUE;

for (Map.Entry<String, Float> i : map.entrySet()) {
for (Map.Entry<String, Float[]> i : map.entrySet()) {
String key = i.getKey();
if (map.get(key) > largestValue)
largestValue = map.get(key);
if (map.get(key)[1] > largestValue)
largestValue = map.get(key)[1];
}
return largestValue;

}

/**
* SmallestMapValue(): Return smallest float value of the provided map
* SmallestMapValue(): Return smallest daily lowvalue of the provided map
*
* @param map
*/
public static Float smallestMapValue(Map<String, Float> map) {
public static Float smallestMapValue(Map<String, Float[]> map) {
Float smallestValue = Float.MAX_VALUE;

for (Map.Entry<String, Float> i : map.entrySet()) {
for (Map.Entry<String, Float[]> i : map.entrySet()) {
String key = i.getKey();
if (map.get(key) < smallestValue)
smallestValue = map.get(key);
if (map.get(key)[2] < smallestValue)
smallestValue = map.get(key)[2];
}
return smallestValue;

Expand All @@ -139,7 +154,7 @@ public static void displayTrendingMap(String symb, String token) {
Float slotsPerCostUnit;

// Get the historical quotes
Map<String, Float> resultTreeMap = getHistoricalQuotes(symb, token);
Map<String, Float[]> resultTreeMap = getHistoricalQuotes(symb, token);

// Calculate the largest and smallest security value in the historical data
Float lv = largestMapValue(resultTreeMap);
Expand All @@ -164,19 +179,42 @@ public static void displayTrendingMap(String symb, String token) {
Output.printColorln(Ansi.Color.WHITE, "+" + "-".repeat(GRAPHWIDTH + 12) + "+\n");

// Display trending title bar
int quoteLength = resultTreeMap.get(resultTreeMap.keySet().toArray()[0].toString())[0].toString().length() + 1;
Output.printColorln(Ansi.Color.CYAN, " ".repeat(12) + sv + " ".repeat(GRAPHWIDTH - sv.toString().length() - lv.toString().length() + 1) + lv);
Output.printColorln(Ansi.Color.CYAN, " ".repeat(11) + "+" + "-".repeat(GRAPHWIDTH + 1) + "+");
Output.printColor(Ansi.Color.CYAN, " ".repeat(11) + "+" + "-".repeat(GRAPHWIDTH + 1) + "+");
Output.printColorln(Ansi.Color.CYAN, " Close" + String.format("%" + quoteLength + "s", "High") + String.format("%" + quoteLength + "s", "Low"));

// Loop through the sorted data and display the graph
for (Map.Entry<String, Float> i : resultTreeMap.entrySet()) {
for (Map.Entry<String, Float[]> i : resultTreeMap.entrySet()) {
String date = i.getKey();
Float value = resultTreeMap.get(date);
int numSpaces = (int) ((value - sv) * slotsPerCostUnit);

// Display the row of data
Output.printColor(Ansi.Color.CYAN, date + " |");
Output.printColor(Ansi.Color.YELLOW, " ".repeat(numSpaces) + "o");
Output.printColorln(Ansi.Color.CYAN, " ".repeat(GRAPHWIDTH - numSpaces) + "| " + String.format("%.2f", value));
Float close = resultTreeMap.get(date)[0];
Float dailyHigh = resultTreeMap.get(date)[1];
Float dailyLow = resultTreeMap.get(date)[2];

// Calculate the number of spaces (slots) until we get to daily low value
int numInitialSpaces = (int) ((dailyLow - sv) * slotsPerCostUnit);

// Calculate number of dashes from low to close
int numLowSpaces = (int) ((close - dailyLow) * slotsPerCostUnit);

// Calculate number of dashes from close to high
int numHighSpaces = (int) ((dailyHigh - close) * slotsPerCostUnit);

// Calculate number of dashes at the end
int numFinalSpaces = (GRAPHWIDTH - numInitialSpaces - numLowSpaces - numHighSpaces);

try {
Output.printColor(Ansi.Color.CYAN, date + " |");
Output.print(" ".repeat(numInitialSpaces));
Output.printColor(Ansi.Color.WHITE, "-".repeat(numLowSpaces));
Output.printColor(Ansi.Color.YELLOW, "o");
Output.printColor(Ansi.Color.WHITE, "-".repeat(numHighSpaces));
Output.print(" ".repeat(numFinalSpaces));
Output.printColorln(Ansi.Color.CYAN,
"| " + String.format("%7.2f", close) + " " + String.format("%7.2f", dailyHigh) + " " + String.format("%7.2f", dailyLow));
} catch (IllegalArgumentException ex) {
System.out.println("**ERROR**");
}
}

// Footer
Expand Down
25 changes: 21 additions & 4 deletions src/main/java/org/fross/quoter/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,15 @@ public class Main {
public static final String PROPERTIES_FILE = "app.properties";
public static final String PREFS_IEXCLOUDTOKEN = "iexcloudtoken";
public static final String PREFS_SAVED_SYMBOLS = "savedsymbols";

public static final String IEXCLOUDPRODURL = "https://cloud.iexapis.com";
public static final String IEXCLOUDSANDBOXURL = "https://sandbox.iexapis.com";
public static String IEXCloudBaseURL = IEXCLOUDPRODURL;

/**
* Main(): Program entry point
*
* @param args
*/
public static void main(String[] args) {
int optionEntry;
String latestTime = "None";
Expand Down Expand Up @@ -80,7 +88,7 @@ public static void main(String[] args) {
}

// Process Command Line Options
Getopt optG = new Getopt("quote", args, "ckdtx:sriDvzh?");
Getopt optG = new Getopt("quote", args, "ckdtx:sriDvzbh?");
while ((optionEntry = optG.getopt()) != -1) {
switch (optionEntry) {
// Turn on Debug Mode
Expand Down Expand Up @@ -149,6 +157,11 @@ public static void main(String[] args) {
Output.enableColor(false);
break;

// Enable IEXCloud Sandbox instead of normal production enviornment
case 'b':
IEXCloudBaseURL = IEXCLOUDSANDBOXURL;
break;

// Access in program help
case '?':
case 'h':
Expand Down Expand Up @@ -226,9 +239,13 @@ public static void main(String[] args) {
Symbol symbolData = new Symbol(currentSymbol, Prefs.queryString(PREFS_IEXCLOUDTOKEN));

// Validate the provided quote is valid
// If invalid, remove it from symbol list so it doesn't get processed later with trend or export
if (symbolData.get("status").compareTo("Error") == 0) {
// Display error and skip to the next iteration
// Display error
Output.printColorln(Ansi.Color.BLUE, "'" + symbolData.get("symbol") + "' is invalid");

// Remove this invalid symbol from the list and continue to the next iteration
j.remove();
continue;
}

Expand Down Expand Up @@ -417,7 +434,7 @@ public static void main(String[] args) {
}
}

// Display trending data if -t was provided and there is at least one symbol
// Display trending data if -t was provided and there is at least one valid symbol
if (trendFlag == true && !symbolList.isEmpty()) {
for (String i : symbolList) {
HistoricalQuotes.displayTrendingMap(i, Prefs.queryString(PREFS_IEXCLOUDTOKEN));
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/fross/quoter/Symbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ protected static String epochTime2String(String epochTime) {
* @return
*/
private static HashMap<String, String> getQuote(String symb, String token) {
String QUOTEURLTEMPLATE = "https://cloud.iexapis.com/stable/stock/SYMBOLHERE/quote?token=TOKENHERE";
String QUOTEURLTEMPLATE = Main.IEXCloudBaseURL + "/stable/stock/SYMBOLHERE/quote?token=TOKENHERE";
String quoteURL = "";
String quoteDetail = "";
HashMap<String, String> returnData = new HashMap<String, String>();
Expand Down

0 comments on commit 311517d

Please sign in to comment.