

数据是个宝,研究少烦恼
挖的深,看的远,找规律
做笔记,练盘感,多总结

★★★★★博文原创不易,源码使用过程中,如有疑问的地方,欢迎大家指正留言交流。喜欢的老铁可以点赞+收藏分享+置顶,小红牛在此表示感谢。★★★★★
量化教程: 教你快速上车,通达信量化平台(TdxQuant太强了,真香啊)
量化教程:通达信量化接口函数TdxQuant+Tkinter综合示例演示(带你速成速学)
量化教程:对(重点指数+板块+概念+细分行业+标的)月线数据回测2.0
量化教程:对大盘指数月线回测 1.0(TdxQuant+pandas+Tkinter)
量化教程:在通达信里面运行Tq策略后,窗口出现闪退的3种解决办法。
股票精简节点记事本 3.0(tkinter+json+reportlab)
固定盈亏模型计算工具 – 使用说明书
1. 功能简介:本工具用于计算在不同初始本金、胜率、盈亏比和固定盈利金额下,达到 100 万元目标 所需的交易笔数期望值,并展示盈利/亏损笔数的期望分布。所有结果以表格形式呈现,支持点击列头排序,程序启动时自动生成文本报告。

3. 界面说明
5. 保存文件
完整源码如下
# -*- coding: utf-8 -*-# @Author : 小红牛# 微信公众号:gxzfp888import mathimport itertoolsimport tkinter as tkfrom tkinter import ttkfrom tkinter import ttk, messageboxclass FixedAmountApp:"""固定盈亏模型 - 结果展示、排序及 TXT 导出"""def __init__(self, root):self.root = rootself.root.title("小红牛微信公众号:gxzfp888——对以下参数组合模型,达到100万所需的笔数,点击表头可排序")self.root.geometry("1100x600")# 列配置self.columns = [('principal', '本金', 100, 'center'),('win_rate', '胜率', 80, 'center'),('rr_ratio', '盈亏比', 80, 'center'),('profit', '盈利金额', 100, 'center'),('loss', '亏损金额', 100, 'center'),('total_rounds', '所需总笔数', 120, 'center'),('win_expected', '期望盈利笔数', 130, 'center'),('loss_expected', '期望亏损笔数', 130, 'center'),]self.col_names = [c[0] for c in self.columns]# 计算原始数据(使用 itertools.product)self.raw_data = self.calculate_data()# 默认按“总笔数升序,∞ 排最后”排序(用于显示和保存)self.data = self.get_default_sorted_data()# 创建 Treeviewself.tree = ttk.Treeview(root, columns=self.col_names, show='headings')vsb = ttk.Scrollbar(root, orient='vertical', command=self.tree.yview)hsb = ttk.Scrollbar(root, orient='horizontal', command=self.tree.xview)self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)self.tree.grid(row=0, column=0, sticky='nsew')vsb.grid(row=0, column=1, sticky='ns')hsb.grid(row=1, column=0, sticky='ew')root.grid_rowconfigure(0, weight=1)root.grid_columnconfigure(0, weight=1)# 设置列标题和宽度for col_id, header, width, anchor in self.columns:self.tree.heading(col_id, text=header, command=lambda c=col_id: self.on_column_click(c))self.tree.column(col_id, width=width, anchor=anchor)self.sort_col = Noneself.sort_reverse = False# 插入数据self.insert_data()# 保存 TXT 文件(调用 save_to_txt 即可)self.save_to_txt() # 如需自动保存,取消注释;或手动调用def calculate_data(self):"""使用 itertools.product 计算所有组合,返回原始数据列表"""TARGET = 1_000_000principals = [100000, 200000, 300000, 500000]win_rates = [0.4, 0.5, 0.6, 0.7]profit_amounts = [1000, 2000, 3000, 4000, 5000, 7000, 10000]rr_ratios = [1, 2, 3, 4]raw = []# 四层循环 → itertools.productfor principal, p, profit, rr in itertools.product(principals, win_rates, profit_amounts, rr_ratios):loss = profit / rrexpected = p * profit - (1 - p) * lossif expected <= 0:total = float('inf') # 无效策略win_exp = Noneloss_exp = Noneelse:need = TARGET - principaltotal = math.ceil(need / expected)if total < 0:total = 0win_exp = round(total * p, 2)loss_exp = round(total * (1 - p), 2)raw.append((principal, p, rr, profit, loss, total, win_exp, loss_exp))return rawdef get_default_sorted_data(self):"""按原始排序规则:总笔数升序,∞(inf)排最后"""finite = [r for r in self.raw_data if r[5] != float('inf')]infinite = [r for r in self.raw_data if r[5] == float('inf')]finite.sort(key=lambda x: x[5]) # 升序return finite + infinitedef format_row(self, row):"""将原始数据行转换为显示字符串列表"""principal, p, rr, profit, loss, total, win_exp, loss_exp = rowreturn (str(principal),f"{p*100:.0f}%",f"{rr}:1",str(profit),f"{loss:.2f}","∞" if total == float('inf') else str(total),"-" if win_exp is None else f"{win_exp:.2f}","-" if loss_exp is None else f"{loss_exp:.2f}",)def insert_data(self):"""清空并重新填充 Treeview"""for item in self.tree.get_children():self.tree.delete(item)for row in self.data:self.tree.insert('', 'end', values=self.format_row(row))def sort_by_column(self, col_name, reverse):"""按列排序 self.data(处理特殊值 inf / None)"""col_idx = self.col_names.index(col_name)if col_name == 'total_rounds':finite = [r for r in self.data if r[col_idx] != float('inf')]infinite = [r for r in self.data if r[col_idx] == float('inf')]finite.sort(key=lambda x: x[col_idx], reverse=reverse)self.data = finite + infiniteelif col_name in ('win_expected', 'loss_expected'):valid = [r for r in self.data if r[col_idx] is not None]invalid = [r for r in self.data if r[col_idx] is None]valid.sort(key=lambda x: x[col_idx], reverse=reverse)self.data = valid + invalidelse:self.data.sort(key=lambda x: x[col_idx], reverse=reverse)self.insert_data()def on_column_click(self, col_name):"""处理列头点击,切换排序方向"""if self.sort_col == col_name:self.sort_reverse = not self.sort_reverseelse:self.sort_col = col_nameself.sort_reverse = Falseself.sort_by_column(self.sort_col, self.sort_reverse)def save_to_txt(self, filename="fixed_amount_results_with_winloss.txt"):"""保存默认排序结果到 TXT 文件(与原始输出格式一致)"""data = self.get_default_sorted_data() # 始终按原始规则排序with open(filename, "w", encoding="utf-8") as f:f.write("说明:\n")f.write("1. 盈利金额固定,亏损金额 = 盈利金额 / 盈亏比\n")f.write("2. 期望净收益 = 胜率×盈利 - 败率×亏损,若≤0则无法达到\n")f.write("3. 所需总笔数 = ceil((目标 - 本金) / 期望净收益)\n")f.write("4. 期望盈利笔数 = 总笔数 × 胜率,期望亏损笔数 = 总笔数 × (1-胜率),均为期望值(保留两位小数)\n")f.write("-" * 90 + "\n")f.write("固定盈亏金额模型 - 达到100万所需的盈利/亏损笔数期望值(每次投入初始本金)\n")f.write("-" * 90 + "\n\n")f.write(f"{'本金':>3}{'胜率':>6}{'盈亏比':>6}{'盈利金额':>6}{'亏损金额':>6}{'所需总笔数':>6}{'期望盈利笔数':>6}{'期望亏损笔数':>6}\n")f.write("-" * 90 + "\n")for row in data:principal, p, rr, profit, loss, total, win_exp, loss_exp = rowwin_rate_str = f"{p*100:.0f}%"rr_str = f"{rr}:1"loss_str = f"{loss:.2f}"total_str = "∞" if total == float('inf') else str(total)win_exp_str = "-" if win_exp is None else f"{win_exp:.2f}"loss_exp_str = "-" if loss_exp is None else f"{loss_exp:.2f}"f.write(f"{principal:>8}{win_rate_str:>6}{rr_str:>8}{profit:>10}{loss_str:>10}{total_str:>12}{win_exp_str:>14}{loss_exp_str:>14}\n")# 使用弹窗提示,替代原来的 printmessagebox.showinfo("提示", f"结果已保存至 {filename}")if __name__ == "__main__":root = tk.Tk()app = FixedAmountApp(root)root.mainloop()
温馨提示:股市有风险,投资需谨慎。本文所写内容仅供粉丝们参考使用,仅为个人研究观点表述,股友们须自己思考与分析股市。
-!! 完毕 ,感谢您的收看!!-
-------★★历史博文集合★★------
