elon_py/max_finance.py
NY 2bf5cf28b7 1.▶️🔄
2.修改项目结构
2025-03-05 10:24:46 +08:00

338 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import random
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
import tkinter as tk
from tkinter import ttk, messagebox
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import json
from datetime import datetime
import os
def trading_simulation(initial_capital, investment_percent, iterations, returns, probs):
"""Simulate a single person's trading strategy with custom returns and probabilities"""
capital = initial_capital
capital_history = [capital]
total_prob = sum(probs)
if total_prob != 1:
probs = [p / total_prob for p in probs]
for _ in range(iterations):
investment = capital * investment_percent
outcome = random.choices(range(len(returns)), weights=probs, k=1)[0]
capital += investment * (returns[outcome] - 1)
capital_history.append(capital)
return capital_history
def simulate_multiple_people(initial_capital, investment_percent, people, iterations, returns, probs):
"""Simulate multiple people trading"""
return [trading_simulation(initial_capital, investment_percent, iterations, returns, probs) for _ in range(people)]
def create_plots(all_histories, investment_percent, iterations, initial_capital):
"""Create evolution and distribution plots with larger size"""
fig1, ax1 = plt.subplots(figsize=(8, 5))
for history in all_histories:
ax1.plot(history, alpha=0.3)
ax1.set_title(f'Capital Evolution ({len(all_histories)} People, {investment_percent * 100}%, {iterations} Trades)')
ax1.set_xlabel('Number of Trades')
ax1.set_ylabel('Capital')
ax1.grid(True)
final_capitals = np.array([history[-1] for history in all_histories])
capital_changes = ((final_capitals - initial_capital) / initial_capital) * 100
log_changes = np.where(
capital_changes >= 0,
np.log10(1 + capital_changes / 100),
-np.log10(1 + np.abs(capital_changes) / 100)
)
kde = gaussian_kde(log_changes)
x_min, x_max = np.percentile(log_changes, [5, 95])
if x_max - x_min < 0.1:
x_min, x_max = min(log_changes, default=-2), max(log_changes, default=2)
x_range = np.linspace(x_min, x_max, 200)
density = kde(x_range)
fig2, ax2 = plt.subplots(figsize=(8, 5))
ax2.plot(x_range, density, color='blue', label='Probability Density')
ax2.fill_between(x_range, 0, density, where=(x_range < 0), color='red', alpha=0.3, label='Loss')
ax2.fill_between(x_range, 0, density, where=(x_range > 0), color='green', alpha=0.3, label='Profit')
ax2.axvline(x=0, color='black', linestyle='--', alpha=0.5, label='No Change (0%)')
ax2.set_title(f'Final Capital Change Distribution ({investment_percent * 100}%)')
ax2.set_xlabel('Log of % Change')
ax2.set_ylabel('Density')
ax2.grid(True)
ax2.legend()
ax2.minorticks_on()
ax2.grid(which='minor', alpha=0.2)
return fig1, fig2
def run_simulation():
"""Run the simulation with user inputs"""
try:
initial_capital = float(entry_initial_capital.get())
investment_percent = float(entry_investment_percent.get()) / 100
people = int(entry_people.get())
iterations = int(entry_iterations.get())
returns = []
probs = []
for return_entry, prob_entry in probability_entries:
return_val = return_entry.get().strip()
prob_val = prob_entry.get().strip()
if not return_val or not prob_val:
raise ValueError("All return and probability fields must be filled.")
returns.append(float(return_val))
probs.append(float(prob_val) / 100)
# 只检查概率是否为负数不限制return
if any(p < 0 for p in probs):
raise ValueError("Probabilities must be non-negative.")
if initial_capital < 0 or investment_percent < 0 or people <= 0 or iterations <= 0:
raise ValueError("Initial capital, investment %, people, and iterations must be positive.")
all_histories = simulate_multiple_people(initial_capital, investment_percent, people, iterations, returns,
probs)
final_capitals = np.array([history[-1] for history in all_histories])
result_text.delete(1.0, tk.END)
result_text.insert(tk.END, f"Initial Capital: {initial_capital:.2f}\n")
result_text.insert(tk.END, f"Investment Percent: {investment_percent * 100:.2f}%\n")
result_text.insert(tk.END, f"Number of People: {people}\n")
result_text.insert(tk.END, f"Number of Trades: {iterations}\n")
result_text.insert(tk.END, f"Average Final Capital: {np.mean(final_capitals):.2f}\n")
result_text.insert(tk.END, f"Median Final Capital: {np.median(final_capitals):.2f}\n")
result_text.insert(tk.END, f"Max Final Capital: {np.max(final_capitals):.2f}\n")
result_text.insert(tk.END, f"Min Final Capital: {np.min(final_capitals):.2f}\n")
result_text.insert(tk.END,
f"Average Capital Change: {((np.mean(final_capitals) - initial_capital) / initial_capital * 100):.2f}%\n")
if save_to_history.get(): # Only save if checkbox is checked
run_data = {
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"initial_capital": initial_capital,
"investment_percent": investment_percent * 100,
"people": people,
"iterations": iterations,
"returns": returns,
"probs": [p * 100 for p in probs],
"avg_final_capital": float(np.mean(final_capitals))
}
run_history.append(run_data)
save_history_to_file()
update_history_dropdown()
for widget in tab1.winfo_children():
widget.destroy()
for widget in tab2.winfo_children():
widget.destroy()
fig1, fig2 = create_plots(all_histories, investment_percent, iterations, initial_capital)
canvas1 = FigureCanvasTkAgg(fig1, master=tab1)
canvas1.draw()
canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=1)
canvas2 = FigureCanvasTkAgg(fig2, master=tab2)
canvas2.draw()
canvas2.get_tk_widget().pack(fill=tk.BOTH, expand=1)
except ValueError as e:
messagebox.showerror("Input Error", f"Invalid input: {e}")
def add_probability_pair(return_val="", prob_val=""):
"""Add a new return-probability pair with optional default values"""
row = len(probability_entries) + 3
return_label = tk.Label(inner_input_frame, text=f"Return {len(probability_entries) + 1}:")
return_label.grid(row=row, column=0, padx=5, pady=2, sticky="e")
return_entry = tk.Entry(inner_input_frame, width=10)
return_entry.insert(0, return_val)
return_entry.grid(row=row, column=1, padx=5, pady=2)
prob_label = tk.Label(inner_input_frame, text=f"Prob {len(probability_entries) + 1} (%):")
prob_label.grid(row=row, column=2, padx=5, pady=2, sticky="e")
prob_entry = tk.Entry(inner_input_frame, width=10)
prob_entry.insert(0, prob_val)
prob_entry.grid(row=row, column=3, padx=5, pady=2)
probability_entries.append((return_entry, prob_entry))
update_input_scrollbar()
def remove_probability_pair():
"""Remove the last return-probability pair"""
if probability_entries:
last_return_entry, last_prob_entry = probability_entries.pop()
last_return_entry.master.grid_forget() # Remove label
last_prob_entry.master.grid_forget() # Remove label
last_return_entry.destroy()
last_prob_entry.destroy()
update_input_scrollbar()
def update_input_scrollbar():
"""Update the scrollbar for input frame"""
inner_input_frame.update_idletasks()
input_canvas.configure(scrollregion=input_canvas.bbox("all"))
def save_history_to_file():
"""Save run history to a text file"""
with open("cache/run_history.txt", "w") as f:
json.dump(run_history, f, indent=4)
def load_history_from_file():
"""Load run history from a text file"""
global run_history
if os.path.exists("cache/run_history.txt"):
with open("cache/run_history.txt", "r") as f:
run_history = json.load(f)
else:
run_history = []
def update_history_dropdown():
"""Update the history dropdown with saved runs"""
history_menu['menu'].delete(0, 'end')
for i, run in enumerate(run_history):
label = f"{run['timestamp']} - Avg: {run['avg_final_capital']:.2f}"
history_menu['menu'].add_command(label=label, command=lambda idx=i: load_history(idx))
def load_history(index):
"""Load a previous run's parameters"""
run = run_history[index]
entry_initial_capital.delete(0, tk.END)
entry_initial_capital.insert(0, str(run['initial_capital']))
entry_investment_percent.delete(0, tk.END)
entry_investment_percent.insert(0, str(run['investment_percent']))
entry_people.delete(0, tk.END)
entry_people.insert(0, str(run['people']))
entry_iterations.delete(0, tk.END)
entry_iterations.insert(0, str(run['iterations']))
for return_entry, prob_entry in probability_entries:
return_entry.master.grid_forget()
prob_entry.master.grid_forget()
return_entry.destroy()
prob_entry.destroy()
probability_entries.clear()
for r, p in zip(run['returns'], run['probs']):
add_probability_pair(str(r), str(p))
# Create GUI window
root = tk.Tk()
root.title("Trading Simulation with Custom Returns")
root.geometry("1200x900")
window_height = 900
top_height = int(window_height * 0.3)
plot_height = int(window_height * 0.7)
# Load history at startup
run_history = []
load_history_from_file()
# Top frame
top_frame = tk.Frame(root, height=top_height)
top_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
top_frame.pack_propagate(False)
# Input frame with scrollbar
input_frame = tk.LabelFrame(top_frame, text="Simulation Parameters", padx=10, pady=10)
input_frame.pack(side=tk.LEFT, fill=tk.Y, expand=1)
input_canvas = tk.Canvas(input_frame)
input_scrollbar = tk.Scrollbar(input_frame, orient="vertical", command=input_canvas.yview)
inner_input_frame = tk.Frame(input_canvas)
input_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
input_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
input_canvas.create_window((0, 0), window=inner_input_frame, anchor="nw")
input_canvas.configure(yscrollcommand=input_scrollbar.set)
tk.Label(inner_input_frame, text="Initial Capital:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
entry_initial_capital = tk.Entry(inner_input_frame, width=15)
entry_initial_capital.insert(0, "10000.0")
entry_initial_capital.grid(row=0, column=1, padx=5, pady=5)
tk.Label(inner_input_frame, text="Investment %:").grid(row=0, column=2, padx=5, pady=5, sticky="e")
entry_investment_percent = tk.Entry(inner_input_frame, width=15)
entry_investment_percent.insert(0, "10.0")
entry_investment_percent.grid(row=0, column=3, padx=5, pady=5)
tk.Label(inner_input_frame, text="People:").grid(row=1, column=0, padx=5, pady=5, sticky="e")
entry_people = tk.Entry(inner_input_frame, width=15)
entry_people.insert(0, "100")
entry_people.grid(row=1, column=1, padx=5, pady=5)
tk.Label(inner_input_frame, text="Trades:").grid(row=1, column=2, padx=5, pady=5, sticky="e")
entry_iterations = tk.Entry(inner_input_frame, width=15)
entry_iterations.insert(0, "100")
entry_iterations.grid(row=1, column=3, padx=5, pady=5)
run_button = tk.Button(inner_input_frame, text="Run Simulation", command=run_simulation)
run_button.grid(row=2, column=0, columnspan=2, pady=5)
add_button = tk.Button(inner_input_frame, text="+", command=lambda: add_probability_pair())
add_button.grid(row=2, column=3, padx=5, pady=5)
remove_button = tk.Button(inner_input_frame, text="-", command=remove_probability_pair)
remove_button.grid(row=2, column=2, padx=5, pady=5)
default_returns = [20.0, 10.0, 5.0, 2.0, 1.5, 1.2, 1.05, 1.0, 0.9, 0.5, 0.4, 0.2, 0.1, -10.0]
default_probs = [0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 20.0, 20.0, 9.0, 15.0, 15.0, 10.0]
probability_entries = []
for r, p in zip(default_returns, default_probs):
add_probability_pair(str(r), str(p))
inner_input_frame.bind("<Configure>", lambda e: update_input_scrollbar())
# Result frame
result_frame = tk.LabelFrame(top_frame, text="Results", padx=10, pady=10)
result_frame.pack(side=tk.RIGHT, fill=tk.Y, expand=1)
result_text = tk.Text(result_frame, height=10, width=40)
result_text.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
result_scrollbar = tk.Scrollbar(result_frame, orient="vertical", command=result_text.yview)
result_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
result_text.configure(yscrollcommand=result_scrollbar.set)
history_frame = tk.Frame(result_frame)
history_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=5)
tk.Label(history_frame, text="Run History:").pack(side=tk.LEFT, padx=5)
history_var = tk.StringVar()
history_menu = tk.OptionMenu(history_frame, history_var, "No runs yet")
history_menu.pack(side=tk.LEFT, fill=tk.X, expand=1)
# Checkbox for saving to history
save_to_history = tk.BooleanVar(value=True) # Default checked
save_checkbox = tk.Checkbutton(history_frame, text="Save to History", variable=save_to_history)
save_checkbox.pack(side=tk.LEFT, padx=5)
# Populate history dropdown with loaded data
update_history_dropdown()
# Plot frame with tabs
plot_outer_frame = tk.LabelFrame(root, text="Plots", padx=10, pady=10, height=plot_height)
plot_outer_frame.pack(fill=tk.BOTH, expand=1)
plot_outer_frame.pack_propagate(False)
notebook = ttk.Notebook(plot_outer_frame)
notebook.pack(fill=tk.BOTH, expand=1)
tab1 = ttk.Frame(notebook)
tab2 = ttk.Frame(notebook)
notebook.add(tab1, text="Capital Evolution")
notebook.add(tab2, text="Distribution")
root.mainloop()