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"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.dsh.openterm"
|
||||
android:versionCode="5"
|
||||
android:versionName="0.4.1">
|
||||
android:versionCode="6"
|
||||
android:versionName="0.4.2">
|
||||
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28" />
|
||||
<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;
|
||||
String highS = "--", lowS = "--", volS = "--", hi52S = "--", lo52S = "--";
|
||||
String currency = "", exchange = "";
|
||||
boolean up, loaded;
|
||||
boolean up, loaded, batchHit;
|
||||
Quote(String yahoo, String tencent, String name) {
|
||||
this.yahoo = yahoo; this.tencent = tencent; this.name = name;
|
||||
this.sym = yahoo;
|
||||
@@ -706,13 +706,17 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
||||
|
||||
void fetchList(List<Quote> list, final boolean mon) {
|
||||
int ok = 0;
|
||||
// 国内快路径:Yahoo 已熔断时先走新浪批量(1 次请求覆盖指数/商品/美股)
|
||||
int batched = 0;
|
||||
if (System.currentTimeMillis() < yahooDeadUntil) batched = fetchSinaBatch(list);
|
||||
for (Quote q : list) {
|
||||
if (q.batchHit) { ok++; q.batchHit = false; continue; }
|
||||
if (fetchYahoo(q)) ok++;
|
||||
else if (fetchEastMoney(q)) ok++;
|
||||
else if (q.tencent != null && fetchTencent(q)) ok++;
|
||||
else if (fetchSinaHf(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;
|
||||
ui.post(new Runnable() { @Override public void run() {
|
||||
@@ -733,6 +737,8 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
||||
}
|
||||
|
||||
boolean fetchYahoo(Quote q) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now < yahooDeadUntil) return false; // 熔断中,直接跳过
|
||||
try {
|
||||
JSONObject meta = chartJson(q.yahoo, "1d", "5m").getJSONObject("chart")
|
||||
.getJSONArray("result").getJSONObject(0).getJSONObject("meta");
|
||||
@@ -740,13 +746,93 @@ public class MainActivity extends Activity implements View.OnClickListener {
|
||||
double prev = meta.optDouble("chartPreviousClose",
|
||||
meta.optDouble("previousClose", Double.NaN));
|
||||
if (Double.isNaN(price) || price == 0) return false;
|
||||
yahooFailStreak = 0;
|
||||
applyQuote(q, price, prev,
|
||||
meta.optString("currency", ""), meta.optString("exchangeName", ""),
|
||||
meta.optDouble("regularMarketDayHigh", Double.NaN),
|
||||
meta.optDouble("regularMarketDayLow", Double.NaN),
|
||||
meta.optDouble("regularMarketVolume", Double.NaN));
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user