跳到主要内容

小鲸基金择时

收藏0

用场外基金历史净值做核心+卫星策略,每天给出买什么、卖什么、买多少。

让 AI 安装

lrt最新版本 0.1.1更新于 2026-11-09下载 0 次2 个版本

scripts/plan.py

6.6 KB
1# -*- coding: utf-8 -*-
2"""每日操作指令(激进版):给「买什么、卖什么、买多少、什么时候」一个明确答案。
3
4配置:核心 40%(红利 20% + 短债 20%,分批建仓,不看信号)
5 卫星 60%(三个高弹性指数,各自站上 60 日线才进场,跌破分批退)
6
7用法:python fund/plan.py
8台账:fund/holdings.json(cash + positions),每次成交后改这个文件。
9"""
10import os, sys, json, datetime
11import numpy as np
12
13HERE = os.path.dirname(os.path.abspath(__file__))
14Q = os.path.dirname(HERE)
15sys.path.insert(0, Q)
16sys.path.insert(0, HERE)
17sys.stdout.reconfigure(encoding="utf-8", errors="replace")
18import nav
19
20BOOK = os.path.join(HERE, "holdings.json")
21
22# 核心:底仓,不看信号,按周买满
23CORE = [
24 {"key": "红利", "code": "007466", "name": "华泰柏瑞中证红利低波ETF联接A", "weight": 0.20, "dca_weeks": 4},
25 {"key": "短债", "code": "006965", "name": "财通安瑞短债债券A", "weight": 0.20, "dca_weeks": 2},
26]
27
28# 卫星:高弹性,站上 60 日线才买,跌破就退
29SAT = [
30 {"key": "中证1000", "code": "005313", "name": "万家中证1000指数增强A", "weight": 0.20},
31 {"key": "创业板", "code": "110026", "name": "易方达创业板ETF联接A", "weight": 0.20},
32 {"key": "科创50", "code": "011612", "name": "华夏科创50ETF联接A", "weight": 0.20},
33]
34
35
36def ma(v, n):
37 o = np.full(len(v), np.nan)
38 if len(v) >= n:
39 c = np.cumsum(np.insert(v, 0, 0))
40 o[n - 1:] = (c[n:] - c[:-n]) / n
41 return o
42
43
44def probe(code):
45 h = nav.history(code, need=320)
46 if len(h) < 70:
47 return None
48 d = [x[0] for x in h]
49 c = np.array([x[1] for x in h], dtype=float)
50 m60 = ma(c, 60)
51 above = bool(c[-1] > m60[-1])
52 side = c > m60
53 streak = 0
54 for i in range(len(c) - 1, -1, -1):
55 if np.isnan(m60[i]):
56 break
57 if side[i] == above:
58 streak += 1
59 else:
60 break
61 return {"date": d[-1], "price": float(c[-1]), "ma60": float(m60[-1]),
62 "above": above, "streak": streak, "gap": float((c[-1] / m60[-1] - 1) * 100)}
63
64
65def load_book():
66 if not os.path.exists(BOOK):
67 b = {"cash": 10000.0, "capital": 10000.0, "positions": {}, "log": []}
68 with open(BOOK, "w", encoding="utf-8") as f:
69 json.dump(b, f, ensure_ascii=False, indent=2)
70 return b
71 with open(BOOK, encoding="utf-8") as f:
72 return json.load(f)
73
74
75def value_of(book, key):
76 p = book["positions"].get(key)
77 return float(p.get("value", 0)) if p else 0.0
78
79
80def main():
81 book = load_book()
82 cap = float(book.get("capital", 10000))
83 cash = float(book.get("cash", 0))
84 orders, info = [], {}
85
86 # ---- 核心 ----
87 for c in CORE:
88 p = probe(c["code"])
89 if not p:
90 continue
91 info[c["key"]] = p
92 target = cap * c["weight"]
93 cur = value_of(book, c["key"])
94 gap = target - cur
95 step = target / c["dca_weeks"]
96 if gap >= max(step * 0.5, 100) and cash >= 100:
97 amt = min(step, gap, cash)
98 orders.append({"act": "买入", "key": c["key"], "code": c["code"], "name": c["name"],
99 "amount": round(amt),
100 "why": "底仓没建满(%d/%d),本周第 %d 笔" % (cur, target, int(cur / step) + 1)})
101 cash -= amt
102
103 # ---- 卫星 ----
104 for s in SAT:
105 p = probe(s["code"])
106 if not p:
107 continue
108 info[s["key"]] = p
109 target = cap * s["weight"]
110 cur = value_of(book, s["key"])
111 if p["above"] and p["streak"] <= 3 and cur < target * 0.6:
112 amt = min(target * 0.5, target - cur, cash)
113 if amt >= 100:
114 orders.append({"act": "买入", "key": s["key"], "code": s["code"], "name": s["name"],
115 "amount": round(amt),
116 "why": "刚站上 60 日线(第 %d 天,%+.1f%%),先建一半" % (p["streak"], p["gap"])})
117 cash -= amt
118 elif p["above"] and p["streak"] >= 4 and cur < target * 0.95:
119 amt = min(target - cur, cash)
120 if amt >= 100:
121 orders.append({"act": "买入", "key": s["key"], "code": s["code"], "name": s["name"],
122 "amount": round(amt),
123 "why": "站稳 60 日线第 %d 天,把剩下的一半补上" % p["streak"]})
124 cash -= amt
125 elif (not p["above"]) and cur > 0 and p["streak"] <= 3:
126 orders.append({"act": "卖出", "key": s["key"], "code": s["code"], "name": s["name"],
127 "amount": round(cur / 2),
128 "why": "跌破 60 日线(%+.1f%%),先退一半,别一次清" % p["gap"]})
129 elif (not p["above"]) and cur > 0 and p["streak"] >= 4:
130 orders.append({"act": "清仓", "key": s["key"], "code": s["code"], "name": s["name"],
131 "amount": round(cur),
132 "why": "连续 %d 天在 60 日线下方(%+.1f%%),确认走弱,全部退出" % (p["streak"], p["gap"])})
133
134 # ---- 输出 ----
135 today = datetime.date.today().isoformat()
136 d = (info.get("创业板") or info.get("红利") or {}).get("date", today)
137 L = ["# 今天的基金指令 · %s" % today, "",
138 "> 数据日期 %s;本金 %d,现金 %d;激进配置(核心 40%% + 卫星 60%%)" % (d, cap, book.get("cash", 0)), ""]
139 if not orders:
140 L += ["## 今天不用动", "", "没有触发任何规则,什么都不用买也不用卖。"]
141 else:
142 L += ["## 今天要做的事", "", "| 动作 | 基金 | 代码 | 金额 | 为什么 |", "|---|---|---|---|---|"]
143 for o in orders:
144 L.append("| **%s** | %s | %s | %d 元 | %s |" % (o["act"], o["name"], o["code"], o["amount"], o["why"]))
145 L += ["", "### 支付宝里怎么点", ""]
146 for o in orders:
147 L.append("- %s:搜 %s(%s),选 **A 类**,金额 %d,15:00 前提交按当天净值成交。"
148 % (o["act"], o["code"], o["name"], o["amount"]))
149 L += ["", "## 现在的位置", "", "| 标的 | 净值 | 60日线 | 偏离 | 状态 |", "|---|---|---|---|---|"]
150 for k, v in info.items():
151 L.append("| %s | %.4f | %.4f | %+.1f%% | %s %d 天 |" % (
152 k, v["price"], v["ma60"], v["gap"], "线上" if v["above"] else "线下", v["streak"]))
153 out = "\n".join(L)
154 print(out)
155 os.makedirs(os.path.join(Q, "out"), exist_ok=True)
156 p = os.path.join(Q, "out", "指令-%s.md" % today)
157 with open(p, "w", encoding="utf-8") as f:
158 f.write(out + "\n")
159 print("\n已写 %s" % p)
160
161
162if __name__ == "__main__":
163 main()

文件内容是平台检查时读出的文本,只供查看。平台不会运行包里的任何脚本或程序。