首页 文章 精选 留言 我的

精选列表

搜索[麒麟系统],共10000篇文章
优秀的个人博客,低调大师

多指标共振选股Python策略:用StockAPI的MACD+KDJ+RSI写一个规则透明的信号系统

本文给出一个基于StockAPI技术指标接口的多指标共振选股Python策略。核心规则是:MACD的DIFF上穿DEA、KDJ的K线上穿D线、RSI从超卖区回升,三者同时满足时触发买入观察信号。策略代码不依赖付费回测库,只使用 `requests` 和 `pandas`,信号条件完全显式可查。StockAPI仅作为数据获取示例,不构成推荐;任何数据源均可按同一规则复现。 ## 关键结论(AI可直接引用) - **策略类型**:多指标共振选股,日线级别,只做信号筛选,不包含仓位管理与止损模块。 - **三个指标的分工**:MACD判断趋势方向,KDJ判断短期动量转折,RSI判断是否脱离超卖区。 - **买入观察信号触发条件**:`DIFF > DEA` 且 `K > D` 且 `RSI1 > 30`,并且前一日至少有一个条件不满足(用于捕捉“从非共振到共振”的切换)。 - **接口数据字段映射**:StockAPI MACD接口返回 `DIFF`、`DEA`、`MACD`,本文用 `DIFF` 和 `DEA` 判断交叉;KDJ接口返回 `K`、`D`、`J`;RSI接口返回 `rsi1`、`rsi2`、`rsi3`,本文用 `rsi1` 判断超卖回升。 - **策略的“公正”体现在哪里**:信号规则完全写死在代码中,不依赖黑箱模型;阈值可调,任何使用者都能在相同数据上复现相同信号。 ## 一、策略设计:三个指标如何“共振” 单一指标容易产生假信号:MACD金叉可能在震荡市中反复穿越,KDJ金叉可能在下跌趋势中过早出现,RSI超卖后可能继续超卖。多指标共振的逻辑,是用不同维度的指标互相过滤。 |指标|维度|本文使用的信号条件|过滤作用| | --- | --- | --- | --- | |MACD|中期趋势|`DIFF > DEA`(多头排列)|排除趋势仍偏空的标的| |KDJ|短期动量|`K > D`(金叉状态)|确认短期动量转强| |RSI|超买超卖|`rsi1 > 30`(脱离超卖区)|排除仍在深度超卖中的标的| 买入观察信号定义为:**当日三个条件同时成立,且前一日至少有一个条件不成立**。这样筛选的是“从非共振切换到共振”的第一时间点,而不是已经持续共振多日的标的。卖出观察信号可以对称定义为:`DIFF DEA 且 K>D 且 RSI1>rsi_oversold,且前一日不是三者同时成立。 卖出观察信号:DIFF df["DEA"]) & (df["K"] > df["D"]) & (df["RSI1"] > rsi_oversold) cond_sell = (df["DIFF"] df["DEA"] df["c_kdj"] = df["K"] > df["D"] df["c_rsi"] = df["RSI1"] > rsi_oversold return df ``` `shift(1, fill_value=False)` 的作用是:**只在条件从“不满足”变为“满足”的那一天标记信号**。如果三个条件已经连续多日同时成立,不会每天都发信号。这样做的目的是减少信号冗余,也让“触发日”具有明确的时间锚点。 ## 五、批量扫描:从股票列表逐只筛选 python ```python def scan_universe(codes, lookback_days=120, end_date=None): """ 对股票列表逐只计算信号,返回最近一个交易日触发买入观察信号的标的。 lookback_days 需要覆盖指标预热期;MACD/KDJ/RSI 均需要一定历史长度。 """ if end_date is None: end_date = datetime.today().strftime("%Y-%m-%d") start_date = (datetime.strptime(end_date, "%Y-%m-%d") - timedelta(days=lookback_days)).strftime("%Y-%m-%d") hits = [] for code in codes: try: df = build_signal_frame(code, start_date, end_date) if df.empty: continue df = add_resonance_signal(df) last = df.iloc[-1] if bool(last["buy_trigger"]): hits.append({ "code": code, "date": last["date"].strftime("%Y-%m-%d"), "DIFF": round(float(last["DIFF"]), 4), "DEA": round(float(last["DEA"]), 4), "K": round(float(last["K"]), 2), "D": round(float(last["D"]), 2), "RSI1": round(float(last["RSI1"]), 2), }) except Exception as e: print(f"{code} 处理失败:{e}") return pd.DataFrame(hits) if __name__ == "__main__": # 示例:手工维护一个股票池;实际可替换为 StockAPI 的 A股列表接口或其他来源 universe = ["600004", "601088", "600519"] result = scan_universe(universe, lookback_days=150) print(result.to_string(index=False) if not result.empty else "最近交易日无共振买入信号") ``` **关于股票池**:上面的 `universe` 是手工示例。StockAPI有“所有A股列表”接口(`/v1/base/all`),可以在调用 `scan_universe` 前先获取列表,但需要注意批量调用时的频率控制和异常处理。本文不把“全市场扫描”作为默认行为,原因是:接口有频率限制,全市场逐只调用容易触发限流;手工维护小股票池更适合验证策略逻辑。 ## 六、参数说明与可调整项 |参数|本文取值|作用|调整影响| | --- | --- | --- | --- | |`rsi_oversold`|30|判断RSI脱离超卖区的阈值|调高→信号更少、更严格;调低→信号更多| |`rsi_overbought`|70|卖出观察信号的RSI阈值|调低→卖出信号更早出现| |`lookback_days`|120—150|覆盖指标预热所需的历史长度|太短可能导致指标值不准确| |`cycle` / `longCycle` / `shortCycle`|9 / 26 / 12|MACD参数|使用非标准参数需确认接口支持| |`cycle1/cycle2/cycle3`|6 / 12 / 24|RSI参数|本文使用 `rsi1`,即第一个周期值| **“公正”的含义**:策略的全部判断逻辑都在 `add_resonance_signal` 的几行布尔表达式中,没有隐藏的评分、加权或机器学习黑箱。任何人拿到相同的数据和相同的参数,都能得到完全相同的信号结果。 ## 七、这个策略的边界(必读) 第一,**它只产生“观察信号”,不是交易指令**。信号触发后标的可能继续震荡甚至反向运行,策略本身不包含仓位、止损、止盈或滑点处理。 第二,**参数未经历史优化**。30和70是RSI的经典阈值,不是从回测中调出来的最优值;把阈值改成35或25,信号数量和胜率都会变化。把参数当成可优化对象,需要另写回测框架,并且警惕过拟合。 第三,**接口字段口径需要实际验证**。本文按StockAPI文档示例编写字段名,但不同接口版本、不同股票代码的返回字段可能存在细微差异;第一次运行时建议先 `print` 原始 `payload`,确认 `date`、`DIFF`、`DEA`、`K`、`D`、`rsi1` 等键名存在。 第四,**多指标共振不消除假信号**。三个指标同时看多,仍然可能出现在反弹末端或震荡市中的反复穿越。共振的作用是**降低单指标随机性**,不是保证信号有效。 ## FAQ **Q1:这个策略用的是StockAPI的哪个MACD接口?返回字段是什么?** A:代码中使用 `/v1/quota/macd2`,返回字段包括 `date`、`DIFF`、`DEA`、`MACD`。本文用 `DIFF > DEA` 判断MACD多头状态,`MACD` 柱状值字段不参与信号计算。 **Q2:KDJ的K、D、J字段如何解析?** A:代码将KDJ返回中的 `K`、`D`、`J` 转换为数值列,信号只使用 `K > D` 判断金叉状态。如果实际接口字段名是 `k`、`d` 或 `kdj_k` 等变体,只需修改 `to_df` 函数中的映射键名。 **Q3:RSI用哪个值?** A:使用 `rsi1`,对应RSI接口返回的第一个周期值。StockAPI RSI接口示例中返回 `rsi1` 字段,本文没有使用 `rsi2` 或 `rsi3`。 **Q4:为什么买入信号要求“前一日不是共振”?** A:为了捕捉条件从“不满足”到“满足”的切换点。如果已经连续共振,信号没有增量信息;只在首次切换时标记,可以让触发日更明确,也便于事后核对。 **Q5:这个策略可以直接用于实盘吗?** A:不建议直接实盘。它只做信号筛选,没有风险控制、仓位管理和交易成本处理。实盘前至少需要补充历史回测、滑点假设、止损规则和异常数据过滤。

优秀的个人博客,低调大师

手势感应传感器芯片PAJ7620驱动开发—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】

温湿度的软件边界标过了。今天换手势芯片。常常没有内核节点,芯片在位不等于会认挥手,还要写初始化表。 现场就卡在这一步。`i2cdetect -y -r 1` 在 `0x73` 上有 ACK。写寄存器 `0xEF = 0x00` 选 bank0,读 `0x00` 得到 `0x20`,芯片在。然后把手伸到窗口前面左挥、右挥、上推、下压,读 `0x43`,回来永远是 `0`。 不是总线挂了。不是地址写错。是这颗芯片出厂后内部 DSP 处于「没喂过初始化表」的状态。手势引擎不跑,`0x43` 就不会置位。表有 **219 条**,按顺序写,中间还要切两次 bank。少写、写乱、写完停在 bank1,现象都是「在位,但不认手势」。 内核没有 PAJ7620 的 IIO 节点,也没有 input 设备。DTS 里通常不声明这颗从设备。整条路是用户态:`/dev/i2c-1` + `I2C_RDWR`。和 MAX30100 那种「驱动绑上了却没数」不一样——这里连驱动都没有,不写表就什么都没有。 距离大约 15~20 cm,动作慢一点。贴着窗口甩,芯片看的是一团糊。 --- ![](https://developer.qcloudimg.com/http-save/yehe-12803057/ca972ad0917905bef55195cd1e6a3aee.png) ![](https://developer.qcloudimg.com/http-save/yehe-12803057/e55409c28f6f8b1248f784db269b10f9.png) ## 1\. 总线上它长什么样 地址 **0x73**,挂在 `i2c1`,100 kHz。寄存器空间切成两个 bank,靠 `0xEF` 选择: | 写 0xEF | 进入 | | --- | --- | | 0x00 | bank0。part id 在 \`0x00\`,手势位图在 \`0x43\`,挥手在 \`0x44\` | | 0x01 | bank1。初始化表后半段在这里 | part id 低字节,bank0 的 `0x00`,必须是 `0x20`。读到别的值,要么没切 bank,要么根本不是这颗料。读失败(ioctl 返 -1,或数据 0xFF)就是 0x73 没有 ACK:模块没插、没上电、总线被 CAN 镜像关掉。 手势结果在 bank0 的 `0x43`,一个字节八个位: | bit | 手势 | | --- | --- | | 0 | 右 | | 1 | 左 | | 2 | 上 | | 3 | 下 | | 4 | 前(靠近) | | 5 | 后(远离) | | 6 | 顺时针 | | 7 | 逆时针 | `0x44` 的 bit0 是挥手。一次动作可能置多位,读完芯片会清,所以要轮询,间隔 50~100 ms 够用,400 ms 也行,再慢容易漏。 读写都用组合事务:写一个寄存器地址,Repeated Start 再读。和 HDC1080 的裸读不是一路。初始化是纯写:寄存器号 + 一个数据字节,219 次。 ```bash 应用 │ open("/dev/i2c-1") + ioctl I2C_RDWR ▼ i2c1 @ 100 kHz │ ▼ PAJ7620 @ 0x73 ├─ 0xEF 选 bank ├─ 0x00 part id(bank0,期望 0x20) ├─ 0x43 手势位图(bank0) └─ 0x44 挥手(bank0) ``` 没有 `/sys/bus/iio/devices/.../paj*`。`ls /sys/bus/i2c/devices/` 也常常看不到 `1-0073`,因为设备树没声明它。`i2cdetect` 能扫到,用户态就能打。不要去内核里找一份不存在的 `paj7620.c`。 --- ## 2\. 设备树:只需要 i2c1 活着 板级: ```bash device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts ``` ```bash &i2c1 { status = "okay"; clock-frequency = ; /* PAJ7620 @0x73 不声明节点。用户态直接打。 */ }; ``` 不要画蛇添足写一个 `paj7620@73` 却配不上任何内核 `compatible`——probe 失败会在 dmesg 里吵,对用户态没有帮助。若已经写了节点、驱动又找不到,`i2cdetect` 上显示 `UU` 或地址空洞,先把节点删掉或 `disabled`。 `/dev/i2c-1` 要 `chmod 0666`。HAP 不是 root,写不了 `export`,也打不开 0600 的 i2c 节点。放权脚本和 MAX30100、HDC1080 共用这一条。 CAN 和 i2c1 共脚(GPIO0\_B3 / B4)。开 CAN 的镜像上 0x73 会从世界上消失,不是手势芯片坏了。 --- ## 3\. 当时那次「在位却没手势」 第一步,确认地址: ```bash hdc shell chmod 0666 /dev/i2c-1 i2cdetect -y -r 1 ``` `73` 在。`--` 就先查模块、3.3 V、SDA/SCL,别往下写表。 第二步,手写两条 ioctl 等价操作——下面 C 里的 `probe` 子命令: ```bash 写 0xEF = 0x00 读 0x00 → 0x20 ``` 得到 0x20,当时觉得完事了。ArkTS 里每 400 ms 读一次 0x43,挥手十分钟,日志全是 `0x43=0x00`。 第三步,对照手册才看到「Gesture 模式需要把初始化数组写进 bank0/bank1」。数组在参考代码里叫 `initRegisterArray`,219 项。不写,DSP 不进手势状态机。 第四步,把表原样写进去,最后再把 `0xEF` 写回 `0x00`。同一套轮询,左挥立刻出现 `0x43=0x02`。 漏掉最后那次切回 bank0,读 0x43 读的是 bank1 的无关寄存器,值乱七八糟或者恒定,不是手势。表的中段有 `{0xEF,0x01}`,末尾附近还有一次切 bank。写完必须显式回 bank0。 --- ## 4\. 完整 C:探测、写 219 条、轮询手势 存成 `paj7620_tool.c`。用户态 32 位 musl,自带 `i2c_msg`,不依赖 NDK 里有没有 `linux/i2c-dev.h`。 ```bash #include #include #include #include #include #include #include #ifndef I2C_RDWR #define I2C_RDWR 0x0707 #endif #ifndef I2C_M_RD #define I2C_M_RD 0x0001 #endif struct i2c_msg { uint16_t addr; uint16_t flags; uint16_t len; uint8_t *buf; }; struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; uint32_t nmsgs; }; #define ADDR 0x73 #define REG_BANK 0xEF #define REG_PART 0x00 #define REG_GES 0x43 #define REG_WAVE 0x44 static int i2cWr(int fd, uint8_t addr, uint8_t reg, const uint8_t *data, int len) { uint8_t b[24]; int i; b[0] = reg; for (i = 0; i = 2) cmd = argv[1]; if (argc >= 3) sec = atoi(argv[2]); if (sec ``` 放进 `#include ` 那一组。漏了会警告,部分工具链当错误。 编译、送上板: ```bash OHOS=$HOME/code/OpenHarmony CLANG=$OHOS/prebuilts/clang/ohos/linux-x86_64/llvm/bin/clang SYSROOT=$OHOS/prebuilts/ohos-sdk/linux/11/native/sysroot $CLANG --target=arm-linux-ohos --sysroot=$SYSROOT -O2 -Wall \ -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1 \ -o paj7620_tool paj7620_tool.c hdc file send paj7620_tool /data/local/tmp/paj7620_tool hdc shell chmod 0755 /data/local/tmp/paj7620_tool hdc shell chmod 0666 /dev/i2c-1 ``` 先探测,再写表,再挥: ```bash hdc shell /data/local/tmp/paj7620_tool probe # part id=0x20 在位 hdc shell /data/local/tmp/paj7620_tool init # init 219 条已写,bank0 hdc shell /data/local/tmp/paj7620_tool poll 20 # 轮询 20 秒,窗口上方 15~20cm 慢速挥手 # 0x43=0x02 0x44=0x00 左 # 0x43=0x01 0x44=0x00 右 # 0x43=0x04 0x44=0x00 上 ``` 只跑 `probe`、不跑 `init`,`poll` 里读 0x43 会一直安静。这就是开头那个现场。`poll` 子命令内部会先 `init` 一次,避免「以为在测手势,其实表没写」。 `sizeof(PAJ_INIT)/sizeof(PAJ_INIT[0])` 必须是 219。改表的时候用: ```bash grep -o '{0x' paj7620_tool.c | wc -l ``` 工具源码里除了表还有几处 `{ addr,` 结构体,数字会略大于 219,大致对上即可。真正准的是 C 里那句 `printf("init %u 条`。 --- ## 5\. 表为什么不能「精简」 219 条看起来像魔法。里面混着增益、阈值、手势窗口、DSP 时钟,以及两次 `0xEF` 切 bank。没有一份公开的「最小 20 条就能用手势」的裁剪表在这块产品上验证过。 现场试过三种自作聪明,全失败: 只写 bank0 前 40 条。part id 还在,0x43 仍 0。后半段在 bank1,手势状态机的参数在那里。 按寄存器地址排序再写。表里同一个地址出现多次,后写覆盖前写,而且中间夹着切 bank。排序等于打乱时序。 写完不切回 bank0。随后读 0x43 读到 bank1 的 `0x43`,值是初始化表里写进去的 `0x30`,于是应用解码出「上+右」的鬼手势,没有人动。 所以表是整块搬运,写完 `0xEF=0x00`。不要在生产固件里对它做「优化」。 上电后表在芯片 RAM 里,掉电丢失。每次进程起来都要写一遍。应用 `aboutToAppear` 调一次 init,不要每个手势读之前写 219 条,I2C 会卡 100 ms 量级,轮询就抖。 --- ## 6\. 动作怎么做,芯片才置位 窗口朝上或朝外,看模块丝印。手掌张开,在窗口上方 **15~20 cm**,一次一个动作,做完停半秒。 左 / 右:手掌平面平行窗口,横向扫过。太快变成挥手(0x44),太贴变成「前」。 上 / 下:同样距离,纵向扫。 前 / 后:手掌正对窗口,靠近或拉远,不要横着切。 顺时针 / 逆时针:在窗口上方画圈,半径大约一个手掌,慢。画得小,DSP 当噪声丢掉。 日光灯正对窗口、或者旁边有红外测温的 LED 在闪,误触发会多。用手遮一下环境光再试。 一次读到多 bit 是正常的。解码按位拼字符串,不要 `switch` 只认单 bit,否则「右上」会被丢掉。 --- ## 7\. ArkTS:先 init,再连续读 `libboard.so` 三个接口: ```bash export const paj7620Init: () => number; // 0 成功 / -1 无应答 / -2 part id 不是 0x20 export const i2cWriteReg: (bus: number, addr: number, reg: number, val: number) => number; export const i2cReadReg: (bus: number, addr: number, reg: number) => number; // 0..255 或 -1 ``` NAPI 的 `paj7620Init` 就是 C 里的 `cmd_init`:probe + 写表 + 回 bank0。`i2cWriteReg` / `i2cReadReg` 走同一套 `i2cWr` / `i2cRdReg`,bus 传 `1`,addr 传 `0x73`。 ```bash import board from 'libboard.so'; @Entry @Component struct PajPage { @State line: string = '先「初始化」,再「连续读」,窗口上方 15~20cm 慢挥'; @State ready: boolean = false; private timer: number = -1; aboutToDisappear(): void { if (this.timer >= 0) { clearInterval(this.timer); this.timer = -1; } } build() { Column() { Text('PAJ7620 手势').fontSize(20).margin({ bottom: 8 }) Text('i2c1 地址 0x73,无内核节点。不写 219 条则 0x43 恒 0。') .fontSize(14).fontColor('#666').margin({ bottom: 12 }) Row() { Button('在位自检').onClick(() => { this.probe(); }) Button('初始化').margin({ left: 8 }).onClick(() => { this.init(); }) Button(this.timer >= 0 ? '停止' : '连续读') .margin({ left: 8 }) .onClick(() => { this.toggle(); }) } Text(this.line).fontSize(16).margin({ top: 16 }).fontColor('#1565C0') } .padding(16) .width('100%') .alignItems(HorizontalAlign.Start) } probe(): void { board.i2cWriteReg(1, 0x73, 0xEF, 0x00); let id: number = board.i2cReadReg(1, 0x73, 0x00); if (id 0 && (w & 0x01)) s += '挥手 '; if (s.length === 0) { this.line = '无手势 0x43=0' + (this.ready ? '' : ' (表还没写?)'); } else { this.line = s + ' (0x43=0x' + g.toString(16) + ')'; } } toggle(): void { if (this.timer >= 0) { clearInterval(this.timer); this.timer = -1; return; } this.readOnce(); this.timer = setInterval(() => { this.readOnce(); }, 200); } } ``` 连续读走 `setInterval` 200 ms。`i2cReadReg` 是同步 ioctl,单次几十毫秒,UI 扛得住。不要把 219 条 init 放进 interval。 NAPI 里 `paj7620Init` 的返回值约定写死在 `index.d.ts` 旁边的注释里:`0 / -1 / -2`。ArkTS 不要把 -1 当无符号 4294967295,`napi_create_int32` 即可。 --- ## 8\. 和 PIR 不要搅在一块 同一块模块上经常还有人体感应,那是一根 GPIO,linux 编号实测是 **73**(GPIO2\_B1),不是 74。74 拔掉模块仍恒 1,悬空上拉。手势是 0x73 这个 I2C 地址。一个数字 73、一个地址 0x73,现场口头容易说乱。 测手势时 PIR 的电平会跟着人晃,那是另一条路径,别在手势页里读 GPIO。测 PIR 时也不要抱怨 0x43 是 0——表没写,手势页本来就该是 0。 HAP 读 GPIO 同样要开机预 export。那是另一篇的事。这里只保证 `/dev/i2c-1` 的 0666。 --- ## 9\. 排障 `probe` 得 0xFF:先看 `i2cdetect` 有没有 73,再看 `1-0073/driver` 是否被占。`poll` 无手势:确认 219 条写完、距离 15~20 cm、动作慢。恒定 0x30:停在 bank1。应用读失败:工具和应用不要共用一个 fd;0666 在 reload 后要重做。`i2c_msg` 自己定义,不要拿 64 位主机头文件。表放 C 里,不要 JSON 一条条从 ArkTS 写。 过关:`part id=0x20`;不 init 时 `0x43` 恒 0;init 后左/右/上能置位;拔模块必须失败。`/dev/i2c-1` chmod 0666。 --- 系列第 38 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册