|
|
网络API数据库定时器股票行情定时采集入库
https://www.yuque.com/aiwork/nba2pr/omtx2y4ssig1mtrr
- /**
- * 案例15:股票行情定时采集入库(纯 printl 日志版,输出到 H5 日志终端)
- *
- * 仿照全网经典实用案例"股票价格监控/盯盘报警",转换成 AIWROK 实现:
- * okHttp 请求免费行情接口(东方财富公开 JSON 接口,UTF-8,无需Key)
- * → 解析最新价/涨跌幅 → SQLite 入库(price_records 表)
- * → 采集结果逐条完整打印(由 LogBridge 打进 H5 终端,紫色主题,教学演示更漂亮)
- *
- * 与前面案例的本质区别:全程不找图、不点击、不依赖无障碍/代理模式,
- * 也没有悬浮窗,是"网络API + 数据库 + 定时器"型自动化,手机放着后台长期跑即可。
- *
- * 这种采集方式的优势:
- * 1. 行情数据持续落库,回看历史波动、做二次分析(均价/振幅/自选股排序)都方便;
- * 2. 换 mysql 模式后数据进局域网/公网数据库,电脑、其他手机、后端 API 都能远程调用同一份数据;
- * 3. 报警逻辑与采集分离:脚本只管采和存,规则改配置区数字即可,不用动逻辑。
- */
- // ========== 配置区(改这里) ==========
- var 自选股列表 = [
- { secid: "1.601318", 名称: "中国平安" }, // 1=沪市 0=深市
- { secid: "1.600036", 名称: "招商银行" },
- { secid: "0.300750", 名称: "宁德时代" }
- ];
- var 采集间隔秒 = 30; // 每 30 秒采一轮(录像教学用;长期挂机可改回 60)
- var 报警涨跌阈值 = 2.0; // |涨跌幅| >= 2% 触发报警(日志打 ⚠)
- var SQLite路径 = "/sdcard/股票行情.db";
- var 表名 = "price_records";
- // ========== 全局变量顶部声明 ==========
- var gStop = false;
- var 数据库连接 = null;
- var 数据库可用 = false;
- var 累计入库条数 = 0;
- // ========== importClass 只能放最外层 ==========
- importClass(android.database.sqlite.SQLiteDatabase);
- importClass(java.io.File);
- // ========== 工具函数(先定义后调用) ==========
- // 连接 SQLite(零配置;如需远程库可仿照案例14扩展 mysql 分支)
- function dbConnect() {
- try {
- 数据库连接 = SQLiteDatabase.openOrCreateDatabase(new File(SQLite路径), null);
- 数据库可用 = true;
- printl("[DB] ✅ SQLite 已打开: " + SQLite路径);
- } catch (e) {
- 数据库可用 = false;
- printl("[DB] ⚠ 打开失败(本轮只打印不入库): " + e);
- }
- }
- // 建表(存在则跳过):code/name/price/change_pct/collected_at
- function dbEnsureTable() {
- if (!数据库可用) return;
- try {
- var sql = "CREATE TABLE IF NOT EXISTS " + 表名 + " (" +
- "id INTEGER PRIMARY KEY AUTOINCREMENT," +
- "code TEXT, name TEXT, price REAL, change_pct REAL, collected_at INTEGER)";
- 数据库连接.execSQL(sql);
- printl("[DB] ✅ 表已就绪: " + 表名);
- } catch (e) {
- printl("[DB] ⚠ 建表失败: " + e);
- 数据库可用 = false;
- }
- }
- // 单只股票一轮行情入库
- function dbInsertQuote(code, name, price, changePct) {
- if (!数据库可用) return false;
- try {
- var stmt = 数据库连接.compileStatement("INSERT INTO " + 表名 +
- " (code, name, price, change_pct, collected_at) VALUES (?,?,?,?,?)");
- stmt.bindString(1, "" + code);
- stmt.bindString(2, "" + name);
- stmt.bindDouble(3, price);
- stmt.bindDouble(4, changePct);
- stmt.bindLong(5, time.nowStamp()); // nowStamp=毫秒数字;time.now()是格式化字符串不能bindLong
- stmt.executeInsert();
- stmt.close();
- return true;
- } catch (e) {
- printl("[DB] ❌ 入库失败: " + e);
- return false;
- }
- }
- function dbClose() {
- if (数据库可用 && 数据库连接 != null) {
- try { 数据库连接.close(); } catch (e) {}
- 数据库连接 = null;
- }
- }
- // 拉取一轮行情:返回数组 [{code,名称,价格,涨跌幅}],失败返回 null
- function 拉取行情() {
- var secids = "";
- for (var i = 0; i < 自选股列表.length; i++) {
- if (i > 0) secids = secids + ",";
- secids = secids + 自选股列表[i].secid;
- }
- var url = "http://push2.eastmoney.com/api/qt/ulist.np/get?fltt=2&secids=" +
- secids + "&fields=f2,f3,f12,f14";
- try {
- printl("📡 正在请求行情接口(自选股 " + 自选股列表.length + " 只)...");
- var http = new okHttp();
- http.setTimeOut(5000, 8000, 5000); // 必须设超时,防网络挂死(踩坑经验)
- printl("📡 等待接口返回(最长8秒)...");
- var text = "" + http.get(url);
- printl("📡 接口已返回,开始解析 JSON...");
- var obj = JSON.parse(text);
- if (!obj || !obj.data || !obj.data.diff) {
- printl("[API] ⚠ 接口无数据(可能停牌/休市字段为空): " + text.substring(0, 120));
- return null;
- }
- var diff = obj.data.diff;
- var 结果 = [];
- for (var j = 0; j < diff.length; j++) {
- var 项 = diff[j];
- var 价格 = parseFloat(项.f2);
- var 涨跌 = parseFloat(项.f3);
- if (isNaN(价格)) 价格 = 0; // "-" 停牌时按 0 处理
- if (isNaN(涨跌)) 涨跌 = 0;
- 结果.push({ code: "" + 项.f12, 名称: "" + 项.f14, 价格: 价格, 涨跌幅: 涨跌 });
- }
- printl("✅ 解析完成,共取到 " + 结果.length + " 只股票行情");
- return 结果;
- } catch (e) {
- printl("[API] ❌ 拉取失败: " + e);
- return null;
- }
- }
- // 逐条完整打印采集结果(演示用:代码/名称/现价/涨跌幅/涨跌方向)
- function 打印采集结果(行情数组) {
- printl("📋 本轮采集结果(共 " + 行情数组.length + " 只):");
- for (var i = 0; i < 行情数组.length; i++) {
- var s = 行情数组[i];
- var 符号 = s.涨跌幅 >= 0 ? "+" : "";
- var 方向 = s.涨跌幅 > 0 ? "🔺涨" : (s.涨跌幅 < 0 ? "🔻跌" : "➖平");
- printl(" [" + (i + 1) + "] " + s.名称 + "(" + s.code + ") 现价 " +
- s.价格.toFixed(2) + " 元 涨跌幅 " + 符号 + s.涨跌幅.toFixed(2) + "% " + 方向);
- }
- }
- // 报警检查:任一股票 |涨跌幅| 超阈值就打 ⚠(与采集/打印分离,改阈值只动配置区)
- function 检查报警(行情数组) {
- var 有报警 = false;
- for (var i = 0; i < 行情数组.length; i++) {
- var s = 行情数组[i];
- if (Math.abs(s.涨跌幅) >= 报警涨跌阈值) {
- 有报警 = true;
- var 符号 = s.涨跌幅 >= 0 ? "+" : "";
- printl("⚠ 报警:" + s.名称 + "(" + s.code + ") 现价 " + s.价格.toFixed(2) +
- " 涨跌幅 " + 符号 + s.涨跌幅.toFixed(2) + "% 超过阈值 " + 报警涨跌阈值 + "%");
- }
- }
- if (!有报警) {
- printl("🔕 报警检查:全部股票 |涨跌幅| < " + 报警涨跌阈值 + "%,无报警");
- }
- }
- // ========== 主流程 ==========
- printl("===== 案例15 股票行情定时采集入库 启动 =====");
- // 步骤0:连库建表
- dbConnect();
- dbEnsureTable();
- printl("⚙ 配置:自选股 " + 自选股列表.length + " 只 · 采集间隔 " + 采集间隔秒 +
- " 秒 · 报警阈值 |涨跌幅| >= " + 报警涨跌阈值 + "%");
- // 停止按钮清理(顺序:先置标志,stopAllRuning 放最后)
- event.onStopBtnEvent(function () {
- gStop = true;
- dbClose();
- runTime.stopAllRuning();
- });
- // 步骤1:定时循环采集
- var 轮次 = 0;
- while (!gStop) {
- 轮次++;
- printl("\n---- 第 " + 轮次 + " 轮采集 ----");
- var 行情数组 = 拉取行情();
- if (行情数组 != null && 行情数组.length > 0) {
- // 1) 逐条完整打印采集结果(演示重点:能看到每只股票的原始数据)
- 打印采集结果(行情数组);
- // 2) 入库
- var 本轮入库 = 0;
- printl("💾 开始逐条写入数据库(表: " + 表名 + ")...");
- for (var k = 0; k < 行情数组.length; k++) {
- var q = 行情数组[k];
- if (dbInsertQuote(q.code, q.名称, q.价格, q.涨跌幅)) {
- 本轮入库++;
- 累计入库条数++;
- printl(" ✓ 已入库 [" + 本轮入库 + "/" + 行情数组.length + "] " +
- q.名称 + "(" + q.code + ") 现价 " + q.价格.toFixed(2) +
- " 涨跌幅 " + q.涨跌幅.toFixed(2) + "%");
- } else {
- printl(" ✗ 入库失败: " + q.名称 + "(" + q.code + ")");
- }
- }
- printl("[DB] 本轮入库 " + 本轮入库 + " 条 | 累计 " + 累计入库条数 + " 条 | 库文件: " + SQLite路径);
- // 3) 报警检查
- 检查报警(行情数组);
- } else {
- printl("⚠ 本轮无数据,跳过入库(" + time.StampToTime(time.nowStamp()) + ")");
- }
- // 分片等待下一轮:每 5 秒打一条进度日志(画面不空,方便观察/录像)
- printl("⏳ 本轮全部完成," + 采集间隔秒 + " 秒后自动开始下一轮");
- var 剩余 = 采集间隔秒;
- while (剩余 > 0 && !gStop) {
- var 步长 = 剩余 >= 5 ? 5 : 剩余;
- printl("⏳ 等待下一轮...剩余 " + 剩余 + " 秒(期间脚本在待机,到点自动拉行情→入库)");
- sleep.second(秒 = 步长);
- 剩余 = 剩余 - 步长;
- }
- }
- dbClose();
- printl("===== 流程结束:共 " + 轮次 + " 轮,累计入库 " + 累计入库条数 + " 条 =====");
复制代码
|
|