v0.4.2: perf - Sina batch quotes (1 request covers indices/commodities/US stocks), Yahoo circuit breaker (2 fails = skip 10min)
This commit is contained in:
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.dsh.openterm"
|
package="com.dsh.openterm"
|
||||||
android:versionCode="5"
|
android:versionCode="6"
|
||||||
android:versionName="0.4.1">
|
android:versionName="0.4.2">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28" />
|
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
|||||||
double priceNum = Double.NaN, prevNum = Double.NaN;
|
double priceNum = Double.NaN, prevNum = Double.NaN;
|
||||||
String highS = "--", lowS = "--", volS = "--", hi52S = "--", lo52S = "--";
|
String highS = "--", lowS = "--", volS = "--", hi52S = "--", lo52S = "--";
|
||||||
String currency = "", exchange = "";
|
String currency = "", exchange = "";
|
||||||
boolean up, loaded;
|
boolean up, loaded, batchHit;
|
||||||
Quote(String yahoo, String tencent, String name) {
|
Quote(String yahoo, String tencent, String name) {
|
||||||
this.yahoo = yahoo; this.tencent = tencent; this.name = name;
|
this.yahoo = yahoo; this.tencent = tencent; this.name = name;
|
||||||
this.sym = yahoo;
|
this.sym = yahoo;
|
||||||
@@ -706,13 +706,17 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
|||||||
|
|
||||||
void fetchList(List<Quote> list, final boolean mon) {
|
void fetchList(List<Quote> list, final boolean mon) {
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
// 国内快路径:Yahoo 已熔断时先走新浪批量(1 次请求覆盖指数/商品/美股)
|
||||||
|
int batched = 0;
|
||||||
|
if (System.currentTimeMillis() < yahooDeadUntil) batched = fetchSinaBatch(list);
|
||||||
for (Quote q : list) {
|
for (Quote q : list) {
|
||||||
|
if (q.batchHit) { ok++; q.batchHit = false; continue; }
|
||||||
if (fetchYahoo(q)) ok++;
|
if (fetchYahoo(q)) ok++;
|
||||||
else if (fetchEastMoney(q)) ok++;
|
else if (fetchEastMoney(q)) ok++;
|
||||||
else if (q.tencent != null && fetchTencent(q)) ok++;
|
else if (q.tencent != null && fetchTencent(q)) ok++;
|
||||||
else if (fetchSinaHf(q)) ok++;
|
else if (fetchSinaHf(q)) ok++;
|
||||||
else if (fetchStooq(q)) ok++;
|
else if (fetchStooq(q)) ok++;
|
||||||
try { Thread.sleep(120); } catch (InterruptedException e) { return; }
|
if (batched == 0) try { Thread.sleep(120); } catch (InterruptedException e) { return; }
|
||||||
}
|
}
|
||||||
final int n = ok;
|
final int n = ok;
|
||||||
ui.post(new Runnable() { @Override public void run() {
|
ui.post(new Runnable() { @Override public void run() {
|
||||||
@@ -733,6 +737,8 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean fetchYahoo(Quote q) {
|
boolean fetchYahoo(Quote q) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now < yahooDeadUntil) return false; // 熔断中,直接跳过
|
||||||
try {
|
try {
|
||||||
JSONObject meta = chartJson(q.yahoo, "1d", "5m").getJSONObject("chart")
|
JSONObject meta = chartJson(q.yahoo, "1d", "5m").getJSONObject("chart")
|
||||||
.getJSONArray("result").getJSONObject(0).getJSONObject("meta");
|
.getJSONArray("result").getJSONObject(0).getJSONObject("meta");
|
||||||
@@ -740,13 +746,93 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
|||||||
double prev = meta.optDouble("chartPreviousClose",
|
double prev = meta.optDouble("chartPreviousClose",
|
||||||
meta.optDouble("previousClose", Double.NaN));
|
meta.optDouble("previousClose", Double.NaN));
|
||||||
if (Double.isNaN(price) || price == 0) return false;
|
if (Double.isNaN(price) || price == 0) return false;
|
||||||
|
yahooFailStreak = 0;
|
||||||
applyQuote(q, price, prev,
|
applyQuote(q, price, prev,
|
||||||
meta.optString("currency", ""), meta.optString("exchangeName", ""),
|
meta.optString("currency", ""), meta.optString("exchangeName", ""),
|
||||||
meta.optDouble("regularMarketDayHigh", Double.NaN),
|
meta.optDouble("regularMarketDayHigh", Double.NaN),
|
||||||
meta.optDouble("regularMarketDayLow", Double.NaN),
|
meta.optDouble("regularMarketDayLow", Double.NaN),
|
||||||
meta.optDouble("regularMarketVolume", Double.NaN));
|
meta.optDouble("regularMarketVolume", Double.NaN));
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) { return false; }
|
} catch (Exception e) {
|
||||||
|
// 连续失败 2 次熔断 10 分钟(国内网络 Yahoo 不可达时省去每标的 ~1s 的等待)
|
||||||
|
if (++yahooFailStreak >= 2) yahooDeadUntil = System.currentTimeMillis() + 10 * 60 * 1000L;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int yahooFailStreak = 0;
|
||||||
|
long yahooDeadUntil = 0;
|
||||||
|
|
||||||
|
/* ── 新浪批量:一次请求覆盖指数/商品/美股 ── */
|
||||||
|
String sinaCodeFor(Quote q) {
|
||||||
|
String y = q.yahoo;
|
||||||
|
if (y.equals("^GSPC")) return "int_sp500";
|
||||||
|
if (y.equals("^IXIC")) return "int_nasdaq";
|
||||||
|
if (y.equals("^DJI")) return "int_dji";
|
||||||
|
if (y.equals("^HSI")) return "int_hangseng";
|
||||||
|
if (y.equals("^N225")) return "int_nikkei";
|
||||||
|
if (y.equals("^FTSE")) return "int_ftse";
|
||||||
|
String hf = sinaHfFor(y);
|
||||||
|
if (!hf.isEmpty()) return hf;
|
||||||
|
if (y.matches("[A-Z]{1,5}")) return "gb_" + y.toLowerCase(Locale.US);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fetchSinaBatch(List<Quote> list) {
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
List<Quote> mappable = new ArrayList<>();
|
||||||
|
for (Quote q : list) {
|
||||||
|
String c = sinaCodeFor(q);
|
||||||
|
if (c != null) { codes.append(c).append(','); mappable.add(q); }
|
||||||
|
}
|
||||||
|
if (mappable.isEmpty()) return 0;
|
||||||
|
codes.deleteCharAt(codes.length() - 1);
|
||||||
|
try {
|
||||||
|
String body = httpGet("https://hq.sinajs.cn/list=" + codes,
|
||||||
|
"https://finance.sina.com.cn");
|
||||||
|
int ok = 0;
|
||||||
|
for (Quote q : mappable) {
|
||||||
|
String tag = "hq_str_" + sinaCodeFor(q) + "=\"";
|
||||||
|
int s = body.indexOf(tag);
|
||||||
|
if (s < 0) continue;
|
||||||
|
int e = body.indexOf('"', s + tag.length());
|
||||||
|
if (e < 0) continue;
|
||||||
|
String[] f = body.substring(s + tag.length(), e).split(",");
|
||||||
|
boolean done = false;
|
||||||
|
String code = sinaCodeFor(q);
|
||||||
|
if (code.startsWith("int_")) { // 指数: 名,价,涨跌,幅度
|
||||||
|
if (f.length >= 4) {
|
||||||
|
double price = Double.parseDouble(f[1]);
|
||||||
|
double chg = Double.parseDouble(f[2]);
|
||||||
|
applyQuote(q, price, price - chg, "", "SINA",
|
||||||
|
Double.NaN, Double.NaN, Double.NaN);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
} else if (code.startsWith("hf_")) { // 商品
|
||||||
|
if (f.length >= 9) {
|
||||||
|
double price = Double.parseDouble(f[0]);
|
||||||
|
double prev = Double.parseDouble(f[7]);
|
||||||
|
if (price != 0) {
|
||||||
|
applyQuote(q, price, prev, "USD", "SINA",
|
||||||
|
Double.parseDouble(f[5]), Double.parseDouble(f[6]), Double.NaN);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (code.startsWith("gb_")) { // 美股: 名,价,幅度,时间,涨跌,开,高,低
|
||||||
|
if (f.length >= 8) {
|
||||||
|
double price = Double.parseDouble(f[1]);
|
||||||
|
if (price != 0) {
|
||||||
|
double chg = Double.parseDouble(f[4]);
|
||||||
|
applyQuote(q, price, price - chg, "USD", "SINA",
|
||||||
|
Double.parseDouble(f[6]), Double.parseDouble(f[7]), Double.NaN);
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (done) { ok++; q.batchHit = true; }
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
} catch (Exception e) { return 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONObject chartJson(String sym, String range, String interval) throws Exception {
|
JSONObject chartJson(String sym, String range, String interval) throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user