#!/usr/bin/env python3 """高逼格 XMind 8 逻辑图:大字号、短文案、深蓝金配色。""" from __future__ import annotations import uuid import zipfile import xml.sax.saxutils as xu from pathlib import Path OUT_DIR = Path(__file__).resolve().parents[1] / "docs" # 商务色:午夜蓝 + 香槟金 + 冷灰层级 C = { "root": dict( fill="#0B1F3A", border="#C9A227", font="#F5F0E6", size="20pt", weight="700", shape="org.xmind.topicShape.roundedRect", ), "L1": dict(size="14pt", weight="700", shape="org.xmind.topicShape.roundedRect"), "L2": dict(size="12pt", weight="600", shape="org.xmind.topicShape.roundedRect"), "L3": dict(size="11pt", weight="600", shape="org.xmind.topicShape.roundedRect"), "leaf": dict( fill="#FFFFFF", border="#E6EAF0", font="#2C3E50", size="11pt", weight="400", shape="org.xmind.topicShape.roundedRect", ), } SECTIONS = [ dict( key="option", title="① 期权", marker="flag-red", fill="#FFF5F5", border="#C0392B", font="#7B241C", kids="option", ), dict( key="perp", title="② 永续合约", marker="flag-blue", fill="#F4F8FC", border="#1F4E79", font="#1A365D", kids="perp", ), dict( key="design", title="③ 设计理念", marker="flag-purple", fill="#F8F5FB", border="#6C3483", font="#4A235A", kids="design", ), dict( key="capital", title="④ 资金要求", marker="flag-green", fill="#F3FAF6", border="#1E8449", font="#145A32", kids="capital", ), dict( key="ops", title="⑤ 操盘思路", marker="flag-orange", fill="#FFFBF2", border="#B9770E", font="#7E5109", kids="ops", ), dict( key="risk", title="⑥ 风险评估", marker="flag-yellow", fill="#FFFCF2", border="#B7950B", font="#7D6608", kids="risk", ), dict( key="discipline", title="⑦ 纪律要求", marker="people-blue", fill="#F4F6F7", border="#566573", font="#2C3E50", kids="discipline", ), ] def nid() -> str: return uuid.uuid4().hex[:16] def sty(sid: str, fill: str, border: str, font: str, size: str, weight: str, shape: str) -> str: return ( f'" ) def styles_xml() -> str: chunks = [ '', '', " ", ' ", " ", " ", " " + sty( "sty-root", C["root"]["fill"], C["root"]["border"], C["root"]["font"], C["root"]["size"], C["root"]["weight"], C["root"]["shape"], ), " " + sty( "sty-leaf", C["leaf"]["fill"], C["leaf"]["border"], C["leaf"]["font"], C["leaf"]["size"], C["leaf"]["weight"], C["leaf"]["shape"], ), " " + sty("sty-pro", "#EAF7EF", "#1E8449", "#145A32", "12pt", "600", C["L2"]["shape"]), " " + sty("sty-con", "#FDEDEC", "#C0392B", "#7B241C", "12pt", "600", C["L2"]["shape"]), " " + sty("sty-sub", "#FFFFFF", "#C9A227", "#0B1F3A", "12pt", "600", C["L2"]["shape"]), ] for s in SECTIONS: chunks.append( " " + sty( f"sty-{s['key']}", s["fill"], s["border"], s["font"], C["L1"]["size"], C["L1"]["weight"], C["L1"]["shape"], ) ) # 操盘二级强调色 for key, fill, border, font in [ ("ops_mind", "#FFF8E1", "#F4D03F", "#7D6608"), ("ops_pos", "#E8F6F3", "#16A085", "#0E6655"), ("ops_open", "#E9F7EF", "#27AE60", "#196F3D"), ("ops_close", "#F5EEF8", "#8E44AD", "#5B2C6F"), ("ops_param", "#EBF5FB", "#2980B9", "#1A5276"), ]: chunks.append( " " + sty(f"sty-{key}", fill, border, font, "12pt", "600", C["L2"]["shape"]) ) chunks.append(" ") chunks.append("\n") return "\n".join(chunks) def topic( title: str, children: list[str] | None = None, *, style_id: str | None = None, marker: str | None = None, structure_class: str | None = None, tid: str | None = None, ) -> str: tid = tid or nid() attrs = [f'id="{tid}"'] if structure_class: attrs.append(f'structure-class="{structure_class}"') if style_id: attrs.append(f'style-id="{style_id}"') body = f"{xu.escape(title)}" if marker: body += f'' if children: body += ( '' + "".join(children) + "" ) return f"{body}" def L(text: str) -> str: """叶子:一句一事,便于放大后清晰。""" return topic(text, style_id="sty-leaf") def G(title: str, lines: list[str], style: str = "sub", marker: str | None = None) -> str: return topic(title, [L(x) for x in lines], style_id=f"sty-{style}", marker=marker) def bodies() -> dict[str, list[str]]: return { "option": [ G( "优势", [ "亏损有界:最多权利金 + 费用", "大波动弹性强,利润空间大", "小资金撬动名义(租金模型)", "按 1 ETH 计价,盈亏直觉清晰", "只做买方,无卖方义务", ], "pro", "symbol-plus", ), G( "劣势", [ "横盘磨损:不动也在付租金", "盘口薄,点差与滑点明显", "平仓依赖买一流动性", "ATM 档位粗时偏离现价", "临近到期,盘口易恶化", ], "con", "symbol-minus", ), ], "perp": [ G( "优势", [ "流动性深,开平接近瞬时", "反向持仓,对冲路径清晰", "杠杆可配(默认 3x)", "与期权组成完整对冲组", ], "pro", "symbol-plus", ), G( "劣势", [ "保证金与强平风险", "实盘有资金费率", "急涨急跌挤压保证金", "横盘几乎不帮权利金回本", "固定 1:2,并非精确中性", ], "con", "symbol-minus", ), ], "design": [ L("结构:ATM 期权买方 + 反向永续"), L("波动大 → 多轮兑现净利"), L("波动小 → 接受权利金磨损"), L("到期自动全平 · 概率样本思维"), L("吃波动,不赌横盘"), G( "权利金 = 租金", [ "统一按 1 ETH 名义核算", "例:20U 租 1870 看涨", "现价到 1970 → 约值 100,毛利约 80", ], ), L("规则确定:周末跳过 · 到期强平"), L("出场看费用后净利,不看虚盈"), L("单组纪律 · 薄腿优先(先期权后永续)"), L("SIM:公共行情 + 本地撮合,不下真单"), ], "capital": [ L("默认仓:永续 1 ETH + 期权 2 ETH · 3x"), G( "单组占用(ETH ≈ 1800–2200)", [ "保证金 ≈ 600–750", "权利金 ≈ 15–60", "费用缓冲 20–50", "波动缓冲 300–800", ], ), G( "建议权益", [ "≥ 1500 仅下限(不推荐)", "≥ 3000 最小可用", "≥ 5000 推荐起步", "≥ 10000 较舒适", ], "pro", "star-green", ), L("仓位减半,资金约减半(费用占比升)"), L("试跑建议:永续 0.1 / 期权 0.2"), L("SIM 权益 10000 ≠ 实盘入金"), L("按磨损日留余量,勿按每组必赚 15"), ], "ops": [ topic( "心法与节奏", [ L("有波动多做,磨盘日可认亏"), L("不追求每日固定轮次"), L("小目标高频兑现(默认 15U)"), L("磨损按预算接受,不报复加仓"), L("全天可开 · 周末默认停开"), L("全平后休息 5 分钟再开下一组"), L("同时最多 1 组,无日轮次上限"), ], style_id="sty-ops_mind", marker="symbol-attention", ), topic( "仓位结构", [ L("永续 1 ETH · 市价 · 3x"), L("期权 2 ETH · 只买"), L("开吃卖一 · 平吃买一"), L("开仓:期权 → 永续(失败回滚)"), L("平仓:期权 → 永续(对冲先留)"), G( "选向(ATM vs 现价)", [ "ATM 偏低 → Call + 空永续", "ATM 偏高 → Put + 多永续", "贴平 → 卖一更高侧开仓", "卖一相等 → 等待", ], ), ], style_id="sty-ops_pos", marker="symbol-plus", ), topic( "开仓", [ G( "前置", [ "策略运行中", "无持仓 · 非休息期", "非周末跳过", ], ), G( "选约", [ "剩余时长 ≥ 12h", "取该到期 ATM", "现价/卖一 ≥ 100", "不合格约 → 等待", ], ), G( "执行", [ "① 吃卖一开期权", "② 市价开永续", "③ 失败则回滚期权", "④ 锁定初始权利金", ], ), ], style_id="sty-ops_open", marker="arrow-up", ), topic( "平仓", [ L("两大类:目标平仓 · 到期平仓"), G( "目标A · 双腿全平", [ "净利达标 + 期权非远虚", "先期权后永续", "流动性闸门:深度 + 标记偏差30%", ], ), G( "目标B · 只平永续", [ "净利达标 + 期权远虚(内在约0)", "100x杠杆下波动约1%常远虚", "期权归档到期 · 不再盯盘", "逐仓不挡下一组开仓", "下一组不扫描残留期权", "下方盘口切回新ATM", "到期按内在价值结算", ], ), G( "到期平仓", [ "目标未达标 → 持有到到期", "期权按内在价值结算", "活跃组若有永续一并平", "残留组只结归档期权", ], ), G( "页面监控分工", [ "有活跃仓 → 钉持仓行权价", "空仓 → 跟现价ATM(≥5点切换)", "仅残留 → 列表显示归档腿", "仅残留 → 下方仍是新ATM", "浮盈亏只用持仓合约买一", ], ), L("另有:紧急全平 / 手动平仓"), ], style_id="sty-ops_close", marker="arrow-down", ), topic( "关键参数", [ L("仓位 1 / 2 · 杠杆 3 · 费率 0.05%"), L("出场 fixed_usdt · 目标 15U"), L("休息 300s · 周末跳过"), L("选约 12h · 杠杆门槛 100"), L("ATM 偏差默认关 · 平仓偏差 30%"), L("行情:OKX 默认 · 币安可切"), ], style_id="sty-ops_param", marker="symbol-question", ), ], "risk": [ G( "主要风险", [ "高 · 横盘磨损权利金", "高 · SIM 难直接外推实盘", "中高 · 方向选错", "中 · 费用侵蚀小目标", "中 · 期权流动性", "中 · 保证金挤压", "中 · 行情中断 / 限流", ], "con", "symbol-exclam", ), G( "缓解", [ "周末跳过 · 到期认亏", "净利达标才兑现", "目标覆盖费用 · 留缓冲", "小资金验证后再放大", ], "pro", "symbol-right", ), G( "边界", [ "不做卖方", "不做横盘智能过滤", "不做多组并行", "实盘另需授权确认", ], ), ], "discipline": [ L("有仓不开下一组"), L("全平后休息满 5 分钟"), L("周末不新开,持仓可平"), L("常规平仓不硬砸流动性"), L("只看出场净利,不看虚盈"), L("磨损日认账,不报复交易"), L("不临时叠仓、不加卖权"), L("SIM 跑通再谈实盘放大"), ], } def mm_escape(s: str) -> str: return xu.escape(s).replace('"', """) def mm(title: str, children: list[str] | None = None) -> str: t = mm_escape(title) if not children: return f'' return f'{"".join(children)}' def mm_flat() -> list[str]: """Freemind:短句版,便于导入后手调样式。""" b = bodies() def walk_xml_titles(xml_topics: list[str]) -> list[str]: # bodies 已是 xmind xml;mm 另建短树 return [] _ = walk_xml_titles return [ mm( "① 期权", [ mm("优势", [mm(x) for x in ["亏损有界", "大波动弹性强", "租金模型", "1 ETH 好算", "只做买方"]]), mm("劣势", [mm(x) for x in ["横盘磨损", "盘口薄", "买一依赖", "ATM 偏离", "临期恶化"]]), ], ), mm( "② 永续合约", [ mm("优势", [mm(x) for x in ["流动性深", "对冲清晰", "杠杆可配", "组成对冲组"]]), mm("劣势", [mm(x) for x in ["强平风险", "资金费率", "保证金挤压", "横盘无助", "1:2 非精确"]]), ], ), mm("③ 设计理念", [mm(x) for x in [ "ATM 买方 + 反向永续", "波动兑现 · 磨损认账", "到期全平 · 概率样本", "权利金 = 租金", "薄腿优先 · 单组纪律", ]]), mm("④ 资金要求", [mm(x) for x in [ "默认 1+2 ETH · 3x", "推荐权益 ≥ 5000", "舒适 ≥ 10000", "试跑 0.1 / 0.2", ]]), mm( "⑤ 操盘思路", [ mm("心法与节奏", [mm(x) for x in ["有波动多做", "目标 15U", "休息 5 分钟", "最多 1 组"]]), mm("仓位结构", [mm(x) for x in ["永续 1 · 期权 2", "先期权后永续", "ATM 选向"]]), mm("开仓", [mm(x) for x in ["前置检查", "选约门槛", "四步执行"]]), mm("平仓", [mm(x) for x in ["目标A双腿", "目标B只平永续", "到期结算", "监控分工"]]), mm("关键参数", [mm(x) for x in ["仓位/杠杆/费率", "目标/休息/周末", "选约/偏差"]]), ], ), mm("⑥ 风险评估", [mm(x) for x in ["横盘磨损", "SIM≠实盘", "方向/流动性/保证金", "残留不挡新开"]]), mm("⑦ 纪律要求", [mm(x) for x in [ "活跃1组 · 残留可共存", "远虚归档 · 不挡新开", "有仓钉持仓 · 空仓跟ATM", "残留列表 · 下方新ATM", "看净利 · 不报复", "SIM 后再放大", ]]), ] def main() -> None: OUT_DIR.mkdir(parents=True, exist_ok=True) data = bodies() branches = [] for s in SECTIONS: branches.append( topic( s["title"], data[s["kids"]], style_id=f"sty-{s['key']}", marker=s["marker"], ) ) # 中心主题:星号标记 = XMind marker-id "star" root = topic( "ETH 永续 + 期权对冲", branches, tid=nid(), structure_class="org.xmind.ui.logic.right", style_id="sty-root", marker="star", ) sheet_id = nid() content = ( '\n' '\n' f' \n' f" {root}\n" " 策略逻辑图 · 高清版\n" " \n" "\n" ) meta = ( '\n' '\n' " eth_hedge_sim\n" "\n" ) manifest = ( '\n' '\n' ' \n' ' \n' ' \n' ' \n' ' \n' "\n" ) xmind_path = OUT_DIR / "ETH对冲策略说明.xmind" with zipfile.ZipFile(xmind_path, "w", compression=zipfile.ZIP_DEFLATED) as zf: zf.writestr("content.xml", content.encode("utf-8")) zf.writestr("styles.xml", styles_xml().encode("utf-8")) zf.writestr("meta.xml", meta.encode("utf-8")) zf.writestr("META-INF/manifest.xml", manifest.encode("utf-8")) mm_path = OUT_DIR / "ETH对冲策略说明.mm" mm_path.write_text( '\n' '\n' f"{mm('★ ETH 永续 + 期权对冲', mm_flat())}\n" "\n", encoding="utf-8", ) print("OK", xmind_path) print("OK", mm_path) print("Root marker: star (星号)") if __name__ == "__main__": main()