import mathimport itertoolsimport tkinter as tkfrom tkinter import ttkfrom tkinter import ttk, messagebox
class FixedAmountApp: """固定盈亏模型 - 结果展示、排序及 TXT 导出"""
def __init__(self, root): self.root = root self.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]
self.raw_data = self.calculate_data()
self.data = self.get_default_sorted_data()
self.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 = None self.sort_reverse = False
self.insert_data()
self.save_to_txt()
def calculate_data(self): """使用 itertools.product 计算所有组合,返回原始数据列表""" TARGET = 1_000_000 principals = [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 = [] for principal, p, profit, rr in itertools.product(principals, win_rates, profit_amounts, rr_ratios): loss = profit / rr expected = p * profit - (1 - p) * loss if expected <= 0: total = float('inf') win_exp = None loss_exp = None else: need = TARGET - principal total = math.ceil(need / expected) if total 0: total = 0 win_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 raw
def 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 + infinite
def format_row(self, row): """将原始数据行转换为显示字符串列表""" principal, p, rr, profit, loss, total, win_exp, loss_exp = row return ( 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 + infinite
elif 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 + invalid
else: 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_reverse else: self.sort_col = col_name self.sort_reverse = False self.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 = row win_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")
messagebox.showinfo("提示", f"结果已保存至 {filename}")
if __name__ == "__main__": root = tk.Tk() app = FixedAmountApp(root) root.mainloop()