/
Utils.java
338 lines (308 loc) · 11.5 KB
/
Utils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
package yahoofinance;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Stijn Strickx
*/
public class Utils {
private static final Logger log = LoggerFactory.getLogger(Utils.class);
public static final BigDecimal HUNDRED = new BigDecimal(100);
public static final BigDecimal THOUSAND = new BigDecimal(1000);
public static final BigDecimal MILLION = new BigDecimal(1000000);
public static final BigDecimal BILLION = new BigDecimal(1000000000);
public static String join(String[] data, String d) {
if (data.length == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
int i;
for (i = 0; i < (data.length - 1); i++) {
sb.append(data[i]).append(d);
}
return sb.append(data[i]).toString();
}
private static String cleanNumberString(String data) {
return Utils.join(data.trim().split(","), "");
}
private static boolean isParseable(String data) {
return !(data == null || data.equals("N/A") || data.equals("-")
|| data.equals("") || data.equals("nan"));
}
public static String getString(String data) {
if(!Utils.isParseable(data)) {
return null;
}
return data;
}
public static BigDecimal getBigDecimal(String data) {
BigDecimal result = null;
if (!Utils.isParseable(data)) {
return result;
}
try {
data = Utils.cleanNumberString(data);
char lastChar = data.charAt(data.length() - 1);
BigDecimal multiplier = BigDecimal.ONE;
switch (lastChar) {
case 'B':
data = data.substring(0, data.length() - 1);
multiplier = BILLION;
break;
case 'M':
data = data.substring(0, data.length() - 1);
multiplier = MILLION;
break;
case 'K':
data = data.substring(0, data.length() - 1);
multiplier = THOUSAND;
break;
}
result = new BigDecimal(data).multiply(multiplier);
} catch (NumberFormatException e) {
log.warn("Failed to parse: " + data);
log.debug("Failed to parse: " + data, e);
}
return result;
}
public static BigDecimal getBigDecimal(String dataMain, String dataSub) {
BigDecimal main = getBigDecimal(dataMain);
BigDecimal sub = getBigDecimal(dataSub);
if(main == null || main.compareTo(BigDecimal.ZERO) == 0) {
return sub;
}
return main;
}
public static double getDouble(String data) {
double result = Double.NaN;
if (!Utils.isParseable(data)) {
return result;
}
try {
data = Utils.cleanNumberString(data);
char lastChar = data.charAt(data.length() - 1);
int multiplier = 1;
switch (lastChar) {
case 'B':
data = data.substring(0, data.length() - 1);
multiplier = 1000000000;
break;
case 'M':
data = data.substring(0, data.length() - 1);
multiplier = 1000000;
break;
case 'K':
data = data.substring(0, data.length() - 1);
multiplier = 1000;
break;
}
result = Double.parseDouble(data) * multiplier;
} catch (NumberFormatException e) {
log.warn("Failed to parse: " + data);
log.debug("Failed to parse: " + data, e);
}
return result;
}
public static Integer getInt(String data) {
Integer result = null;
if (!Utils.isParseable(data)) {
return result;
}
try {
data = Utils.cleanNumberString(data);
result = Integer.parseInt(data);
} catch (NumberFormatException e) {
log.warn("Failed to parse: " + data);
log.debug("Failed to parse: " + data, e);
}
return result;
}
public static Long getLong(String data) {
Long result = null;
if (!Utils.isParseable(data)) {
return result;
}
try {
data = Utils.cleanNumberString(data);
result = Long.parseLong(data);
} catch (NumberFormatException e) {
log.warn("Failed to parse: " + data);
log.debug("Failed to parse: " + data, e);
}
return result;
}
public static BigDecimal getPercent(BigDecimal numerator, BigDecimal denominator) {
if (denominator == null || numerator == null || denominator.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
return numerator.divide(denominator, 4, RoundingMode.HALF_EVEN)
.multiply(HUNDRED).setScale(2, RoundingMode.HALF_EVEN);
}
public static double getPercent(double numerator, double denominator) {
if (denominator == 0) {
return 0;
}
return (numerator / denominator) * 100;
}
private static String getDividendDateFormat(String date) {
if (date.matches("[0-9][0-9]-...-[0-9][0-9]")) {
return "dd-MMM-yy";
} else if (date.matches("[0-9]-...-[0-9][0-9]")) {
return "d-MMM-yy";
} else if (date.matches("...[ ]+[0-9]+")) {
return "MMM d";
} else {
return "M/d/yy";
}
}
/**
* Used to parse the dividend dates. Returns null if the date cannot be
* parsed.
*
* @param date String received that represents the date
* @return Calendar object representing the parsed date
*/
public static Calendar parseDividendDate(String date) {
if (!Utils.isParseable(date)) {
return null;
}
date = date.trim();
SimpleDateFormat format = new SimpleDateFormat(Utils.getDividendDateFormat(date), Locale.US);
format.setTimeZone(TimeZone.getTimeZone(YahooFinance.TIMEZONE));
try {
Calendar today = Calendar.getInstance(TimeZone.getTimeZone(YahooFinance.TIMEZONE));
Calendar parsedDate = Calendar.getInstance(TimeZone.getTimeZone(YahooFinance.TIMEZONE));
parsedDate.setTime(format.parse(date));
if (parsedDate.get(Calendar.YEAR) == 1970) {
// Not really clear which year the dividend date is... making a reasonable guess.
int monthDiff = parsedDate.get(Calendar.MONTH) - today.get(Calendar.MONTH);
int year = today.get(Calendar.YEAR);
if (monthDiff > 6) {
year -= 1;
} else if (monthDiff < -6) {
year += 1;
}
parsedDate.set(Calendar.YEAR, year);
}
return parsedDate;
} catch (ParseException ex) {
log.warn("Failed to parse dividend date: " + date);
log.debug("Failed to parse dividend date: " + date, ex);
return null;
}
}
/**
* Used to parse the last trade date / time. Returns null if the date / time
* cannot be parsed.
*
* @param date String received that represents the date
* @param time String received that represents the time
* @param timeZone time zone to use for parsing the date time
* @return Calendar object with the parsed datetime
*/
public static Calendar parseDateTime(String date, String time, TimeZone timeZone) {
String datetime = date + " " + time;
SimpleDateFormat format = new SimpleDateFormat("M/d/yyyy h:mma", Locale.US);
format.setTimeZone(timeZone);
try {
if (Utils.isParseable(date) && Utils.isParseable(time)) {
Calendar c = Calendar.getInstance();
c.setTime(format.parse(datetime));
return c;
}
} catch (ParseException ex) {
log.warn("Failed to parse datetime: " + datetime);
log.debug("Failed to parse datetime: " + datetime, ex);
}
return null;
}
public static Calendar parseHistDate(String date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
try {
if (Utils.isParseable(date)) {
Calendar c = Calendar.getInstance();
c.setTime(format.parse(date));
return c;
}
} catch (ParseException ex) {
log.warn("Failed to parse hist date: " + date);
log.debug("Failed to parse hist date: " + date, ex);
}
return null;
}
public static Calendar unixToCalendar(long timestamp) {
log.debug("unixToCalendar " + timestamp);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp * 1000);
return calendar;
}
public static String getURLParameters(Map<String, String> params) {
StringBuilder sb = new StringBuilder();
for (Entry<String, String> entry : params.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
String key = entry.getKey();
String value = entry.getValue();
try {
key = URLEncoder.encode(key, "UTF-8");
value = URLEncoder.encode(value, "UTF-8");
} catch (UnsupportedEncodingException ex) {
log.error(ex.getMessage(), ex);
// Still try to continue with unencoded values
}
sb.append(String.format("%s=%s", key, value));
}
return sb.toString();
}
/**
* Strips the unwanted chars from a line returned in the CSV
* Used for parsing the FX CSV lines
*
* @param line the original CSV line
* @return the stripped line
*/
public static String stripOverhead(String line) {
return line.replaceAll("\"", "");
}
public static String unescape(String data) {
StringBuilder buffer = new StringBuilder(data.length());
for (int i = 0; i < data.length(); i++) {
if ((int) data.charAt(i) > 256) {
buffer.append("\\u").append(Integer.toHexString((int) data.charAt(i)));
} else {
if (data.charAt(i) == '\n') {
buffer.append("\\n");
} else if (data.charAt(i) == '\t') {
buffer.append("\\t");
} else if (data.charAt(i) == '\r') {
buffer.append("\\r");
} else if (data.charAt(i) == '\b') {
buffer.append("\\b");
} else if (data.charAt(i) == '\f') {
buffer.append("\\f");
} else if (data.charAt(i) == '\'') {
buffer.append("\\'");
} else if (data.charAt(i) == '\"') {
buffer.append("\\\"");
} else if (data.charAt(i) == '\\') {
buffer.append("\\\\");
} else {
buffer.append(data.charAt(i));
}
}
}
return buffer.toString();
}
}