找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1|回复: 0

网络API数据库定时器股票行情定时采集入库

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
网络API数据库定时器股票行情定时采集入库

QQ截图20261019081758.png

QQ截图20261019081740.png

QQ截图20261019081730.png

QQ截图20261019081712.png

https://www.yuque.com/aiwork/nba2pr/omtx2y4ssig1mtrr



  1. /**
  2. * 案例15:股票行情定时采集入库(纯 printl 日志版,输出到 H5 日志终端)
  3. *
  4. * 仿照全网经典实用案例"股票价格监控/盯盘报警",转换成 AIWROK 实现:
  5. *   okHttp 请求免费行情接口(东方财富公开 JSON 接口,UTF-8,无需Key)
  6. *   → 解析最新价/涨跌幅 → SQLite 入库(price_records 表)
  7. *   → 采集结果逐条完整打印(由 LogBridge 打进 H5 终端,紫色主题,教学演示更漂亮)
  8. *
  9. * 与前面案例的本质区别:全程不找图、不点击、不依赖无障碍/代理模式,
  10. * 也没有悬浮窗,是"网络API + 数据库 + 定时器"型自动化,手机放着后台长期跑即可。
  11. *
  12. * 这种采集方式的优势:
  13. * 1. 行情数据持续落库,回看历史波动、做二次分析(均价/振幅/自选股排序)都方便;
  14. * 2. 换 mysql 模式后数据进局域网/公网数据库,电脑、其他手机、后端 API 都能远程调用同一份数据;
  15. * 3. 报警逻辑与采集分离:脚本只管采和存,规则改配置区数字即可,不用动逻辑。
  16. */

  17. // ========== 配置区(改这里) ==========
  18. var 自选股列表 = [
  19.     { secid: "1.601318", 名称: "中国平安" },   // 1=沪市 0=深市
  20.     { secid: "1.600036", 名称: "招商银行" },
  21.     { secid: "0.300750", 名称: "宁德时代" }
  22. ];
  23. var 采集间隔秒 = 30;            // 每 30 秒采一轮(录像教学用;长期挂机可改回 60)
  24. var 报警涨跌阈值 = 2.0;         // |涨跌幅| >= 2% 触发报警(日志打 ⚠)
  25. var SQLite路径 = "/sdcard/股票行情.db";
  26. var 表名 = "price_records";

  27. // ========== 全局变量顶部声明 ==========
  28. var gStop = false;
  29. var 数据库连接 = null;
  30. var 数据库可用 = false;
  31. var 累计入库条数 = 0;

  32. // ========== importClass 只能放最外层 ==========
  33. importClass(android.database.sqlite.SQLiteDatabase);
  34. importClass(java.io.File);

  35. // ========== 工具函数(先定义后调用) ==========

  36. // 连接 SQLite(零配置;如需远程库可仿照案例14扩展 mysql 分支)
  37. function dbConnect() {
  38.     try {
  39.         数据库连接 = SQLiteDatabase.openOrCreateDatabase(new File(SQLite路径), null);
  40.         数据库可用 = true;
  41.         printl("[DB] ✅ SQLite 已打开: " + SQLite路径);
  42.     } catch (e) {
  43.         数据库可用 = false;
  44.         printl("[DB] ⚠ 打开失败(本轮只打印不入库): " + e);
  45.     }
  46. }

  47. // 建表(存在则跳过):code/name/price/change_pct/collected_at
  48. function dbEnsureTable() {
  49.     if (!数据库可用) return;
  50.     try {
  51.         var sql = "CREATE TABLE IF NOT EXISTS " + 表名 + " (" +
  52.             "id INTEGER PRIMARY KEY AUTOINCREMENT," +
  53.             "code TEXT, name TEXT, price REAL, change_pct REAL, collected_at INTEGER)";
  54.         数据库连接.execSQL(sql);
  55.         printl("[DB] ✅ 表已就绪: " + 表名);
  56.     } catch (e) {
  57.         printl("[DB] ⚠ 建表失败: " + e);
  58.         数据库可用 = false;
  59.     }
  60. }

  61. // 单只股票一轮行情入库
  62. function dbInsertQuote(code, name, price, changePct) {
  63.     if (!数据库可用) return false;
  64.     try {
  65.         var stmt = 数据库连接.compileStatement("INSERT INTO " + 表名 +
  66.             " (code, name, price, change_pct, collected_at) VALUES (?,?,?,?,?)");
  67.         stmt.bindString(1, "" + code);
  68.         stmt.bindString(2, "" + name);
  69.         stmt.bindDouble(3, price);
  70.         stmt.bindDouble(4, changePct);
  71.         stmt.bindLong(5, time.nowStamp());   // nowStamp=毫秒数字;time.now()是格式化字符串不能bindLong
  72.         stmt.executeInsert();
  73.         stmt.close();
  74.         return true;
  75.     } catch (e) {
  76.         printl("[DB] ❌ 入库失败: " + e);
  77.         return false;
  78.     }
  79. }

  80. function dbClose() {
  81.     if (数据库可用 && 数据库连接 != null) {
  82.         try { 数据库连接.close(); } catch (e) {}
  83.         数据库连接 = null;
  84.     }
  85. }

  86. // 拉取一轮行情:返回数组 [{code,名称,价格,涨跌幅}],失败返回 null
  87. function 拉取行情() {
  88.     var secids = "";
  89.     for (var i = 0; i < 自选股列表.length; i++) {
  90.         if (i > 0) secids = secids + ",";
  91.         secids = secids + 自选股列表[i].secid;
  92.     }
  93.     var url = "http://push2.eastmoney.com/api/qt/ulist.np/get?fltt=2&secids=" +
  94.         secids + "&fields=f2,f3,f12,f14";

  95.     try {
  96.         printl("📡 正在请求行情接口(自选股 " + 自选股列表.length + " 只)...");
  97.         var http = new okHttp();
  98.         http.setTimeOut(5000, 8000, 5000);   // 必须设超时,防网络挂死(踩坑经验)
  99.         printl("📡 等待接口返回(最长8秒)...");
  100.         var text = "" + http.get(url);
  101.         printl("📡 接口已返回,开始解析 JSON...");
  102.         var obj = JSON.parse(text);
  103.         if (!obj || !obj.data || !obj.data.diff) {
  104.             printl("[API] ⚠ 接口无数据(可能停牌/休市字段为空): " + text.substring(0, 120));
  105.             return null;
  106.         }
  107.         var diff = obj.data.diff;
  108.         var 结果 = [];
  109.         for (var j = 0; j < diff.length; j++) {
  110.             var 项 = diff[j];
  111.             var 价格 = parseFloat(项.f2);
  112.             var 涨跌 = parseFloat(项.f3);
  113.             if (isNaN(价格)) 价格 = 0;      // "-" 停牌时按 0 处理
  114.             if (isNaN(涨跌)) 涨跌 = 0;
  115.             结果.push({ code: "" + 项.f12, 名称: "" + 项.f14, 价格: 价格, 涨跌幅: 涨跌 });
  116.         }
  117.         printl("✅ 解析完成,共取到 " + 结果.length + " 只股票行情");
  118.         return 结果;
  119.     } catch (e) {
  120.         printl("[API] ❌ 拉取失败: " + e);
  121.         return null;
  122.     }
  123. }

  124. // 逐条完整打印采集结果(演示用:代码/名称/现价/涨跌幅/涨跌方向)
  125. function 打印采集结果(行情数组) {
  126.     printl("📋 本轮采集结果(共 " + 行情数组.length + " 只):");
  127.     for (var i = 0; i < 行情数组.length; i++) {
  128.         var s = 行情数组[i];
  129.         var 符号 = s.涨跌幅 >= 0 ? "+" : "";
  130.         var 方向 = s.涨跌幅 > 0 ? "🔺涨" : (s.涨跌幅 < 0 ? "🔻跌" : "➖平");
  131.         printl("   [" + (i + 1) + "] " + s.名称 + "(" + s.code + ") 现价 " +
  132.             s.价格.toFixed(2) + " 元 涨跌幅 " + 符号 + s.涨跌幅.toFixed(2) + "% " + 方向);
  133.     }
  134. }

  135. // 报警检查:任一股票 |涨跌幅| 超阈值就打 ⚠(与采集/打印分离,改阈值只动配置区)
  136. function 检查报警(行情数组) {
  137.     var 有报警 = false;
  138.     for (var i = 0; i < 行情数组.length; i++) {
  139.         var s = 行情数组[i];
  140.         if (Math.abs(s.涨跌幅) >= 报警涨跌阈值) {
  141.             有报警 = true;
  142.             var 符号 = s.涨跌幅 >= 0 ? "+" : "";
  143.             printl("⚠ 报警:" + s.名称 + "(" + s.code + ") 现价 " + s.价格.toFixed(2) +
  144.                 " 涨跌幅 " + 符号 + s.涨跌幅.toFixed(2) + "% 超过阈值 " + 报警涨跌阈值 + "%");
  145.         }
  146.     }
  147.     if (!有报警) {
  148.         printl("🔕 报警检查:全部股票 |涨跌幅| < " + 报警涨跌阈值 + "%,无报警");
  149.     }
  150. }

  151. // ========== 主流程 ==========
  152. printl("===== 案例15 股票行情定时采集入库 启动 =====");

  153. // 步骤0:连库建表
  154. dbConnect();
  155. dbEnsureTable();
  156. printl("⚙ 配置:自选股 " + 自选股列表.length + " 只 · 采集间隔 " + 采集间隔秒 +
  157.     " 秒 · 报警阈值 |涨跌幅| >= " + 报警涨跌阈值 + "%");

  158. // 停止按钮清理(顺序:先置标志,stopAllRuning 放最后)
  159. event.onStopBtnEvent(function () {
  160.     gStop = true;
  161.     dbClose();
  162.     runTime.stopAllRuning();
  163. });

  164. // 步骤1:定时循环采集
  165. var 轮次 = 0;
  166. while (!gStop) {
  167.     轮次++;
  168.     printl("\n---- 第 " + 轮次 + " 轮采集 ----");

  169.     var 行情数组 = 拉取行情();
  170.     if (行情数组 != null && 行情数组.length > 0) {
  171.         // 1) 逐条完整打印采集结果(演示重点:能看到每只股票的原始数据)
  172.         打印采集结果(行情数组);

  173.         // 2) 入库
  174.         var 本轮入库 = 0;
  175.         printl("💾 开始逐条写入数据库(表: " + 表名 + ")...");
  176.         for (var k = 0; k < 行情数组.length; k++) {
  177.             var q = 行情数组[k];
  178.             if (dbInsertQuote(q.code, q.名称, q.价格, q.涨跌幅)) {
  179.                 本轮入库++;
  180.                 累计入库条数++;
  181.                 printl("   ✓ 已入库 [" + 本轮入库 + "/" + 行情数组.length + "] " +
  182.                     q.名称 + "(" + q.code + ") 现价 " + q.价格.toFixed(2) +
  183.                     " 涨跌幅 " + q.涨跌幅.toFixed(2) + "%");
  184.             } else {
  185.                 printl("   ✗ 入库失败: " + q.名称 + "(" + q.code + ")");
  186.             }
  187.         }
  188.         printl("[DB] 本轮入库 " + 本轮入库 + " 条 | 累计 " + 累计入库条数 + " 条 | 库文件: " + SQLite路径);

  189.         // 3) 报警检查
  190.         检查报警(行情数组);
  191.     } else {
  192.         printl("⚠ 本轮无数据,跳过入库(" + time.StampToTime(time.nowStamp()) + ")");
  193.     }

  194.     // 分片等待下一轮:每 5 秒打一条进度日志(画面不空,方便观察/录像)
  195.     printl("⏳ 本轮全部完成," + 采集间隔秒 + " 秒后自动开始下一轮");
  196.     var 剩余 = 采集间隔秒;
  197.     while (剩余 > 0 && !gStop) {
  198.         var 步长 = 剩余 >= 5 ? 5 : 剩余;
  199.         printl("⏳ 等待下一轮...剩余 " + 剩余 + " 秒(期间脚本在待机,到点自动拉行情→入库)");
  200.         sleep.second(秒 = 步长);
  201.         剩余 = 剩余 - 步长;
  202.     }
  203. }

  204. dbClose();
  205. printl("===== 流程结束:共 " + 轮次 + " 轮,累计入库 " + 累计入库条数 + " 条 =====");
复制代码


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|

GMT+8, 2026-10-20 08:12

宣传软件 · 信息发布软件 · b2b软件 · 广告发布软件

快速回复 返回顶部 返回列表