Python/파이썬과 주식, 코인

파이썬으로 만들어 본 주식 계산기

컴닥 2025. 3. 28. 17:10
반응형

1. 매수 매도 가격 입력 -> 예상 수익률
2. 매수 가격, 목표 수익률 입력 -> 매도 가격 

매수 매도 수수료 및 증권 거래세를 포함.
다만 실제 거래에서는 10원 단위 절사 등을 하는데, 완벽하게 같지는 않습니다.  
참고만 하시구요. 

혹시 오류가 있다면 리플 남겨주세요. 

pip install ttkbootstrap

import os
import tkinter as tk
from tkinter import font, ttk

from ttkbootstrap import Style

BUY_FEE = 0.015
SELL_FEE = 0.015
TAX = 0.15


def on_radiobutton_click(*args):
    if selected_option_var.get() == 'profit_rate':
        buy_price_label.configure(font=BOLD_FONT)
        sell_price_label.configure(font=BOLD_FONT)
        quantity_label.configure(font=BOLD_FONT)
        profit_rate_label.configure(font=DEFAULT_FONT)
    elif selected_option_var.get() == 'sell_price':
        buy_price_label.configure(font=BOLD_FONT)
        sell_price_label.configure(font=DEFAULT_FONT)
        quantity_label.configure(font=BOLD_FONT)
        profit_rate_label.configure(font=BOLD_FONT)


def on_key_release(entry):
    format_number(entry)
    if selected_option_var.get() == 'profit_rate':
        calculate_profit()
    elif selected_option_var.get() == 'sell_price':
        calculate_sell_price()


def format_number(entry):
    value = entry.get().replace(',', '')
    if value.isdigit():
        formatted_value = f'{int(value):,}'
        entry.delete(0, tk.END)
        entry.insert(0, formatted_value)


def calculate_sell_price(*args):
    try:
        buy_price = float(buy_price_entry.get().replace(',', ''))
        quantity = int(quantity_entry.get().replace(',', ''))
        profit_rate = float(profit_rate_entry.get()) / 100
        buy_fee_rate = float(buy_fee_entry.get()) / 100
        sell_fee_rate = float(sell_fee_entry.get()) / 100
        tax_rate = float(tax_entry.get()) / 100

        total_buy = buy_price * quantity * (1 + buy_fee_rate)
        total_buy_fee = buy_price * quantity * buy_fee_rate
        sell_price = buy_price * (1 + buy_fee_rate) * (1 + profit_rate) / (1 - sell_fee_rate - tax_rate)
        total_sell = sell_price * quantity * (1 - sell_fee_rate - tax_rate)
        total_sell_fee_tax = sell_price * quantity * (sell_fee_rate + tax_rate)
        profit = total_sell - total_buy

        sell_price_entry.delete(0, tk.END)
        sell_price_entry.insert(0, f'{sell_price:,.2f}')
        fee_entry.delete(0, tk.END)
        fee_entry.insert(0, f'{total_buy_fee + total_sell_fee_tax:,.2f}')
        profit_entry.delete(0, tk.END)
        profit_entry.insert(0, f'{profit:,.2f}')
    except Exception as e:
        print(e)


def calculate_profit(*args):
    try:
        buy_price = float(buy_price_entry.get().replace(',', ''))
        sell_price = float(sell_price_entry.get().replace(',', ''))
        quantity = int(quantity_entry.get().replace(',', ''))
        buy_fee_rate = float(buy_fee_entry.get()) / 100
        sell_fee_rate = float(sell_fee_entry.get()) / 100
        tax_rate = float(tax_entry.get()) / 100

        total_buy = buy_price * quantity * (1 + buy_fee_rate)
        total_buy_fee = buy_price * quantity * buy_fee_rate
        total_sell = sell_price * quantity * (1 - sell_fee_rate - tax_rate)
        total_sell_fee_tax = sell_price * quantity * (sell_fee_rate + tax_rate)
        profit = total_sell - total_buy
        profit_rate = (profit / total_buy) * 100

        fee_entry.delete(0, tk.END)
        fee_entry.insert(0, f'{total_buy_fee + total_sell_fee_tax:,.2f}')
        profit_entry.delete(0, tk.END)
        profit_entry.insert(0, f'{profit:,.2f}')
        profit_rate_entry.delete(0, tk.END)
        profit_rate_entry.insert(0, f'{profit_rate:,.2f}')
    except Exception as e:
        print(e)


def toggle_always_on_top():
    if always_on_top_var.get():
        root.wm_attributes('-topmost', 1)  # 항상 위
    else:
        root.wm_attributes('-topmost', 0)  # 기본 상태


root = tk.Tk()
root.title('주식 계산기')
root.wm_attributes('-topmost', 1)

if os.path.isfile('icon.ico'):
    root.iconbitmap('icon.ico')

style = Style(theme='superhero')

DEFAULT_FONT = font.nametofont("TkDefaultFont")
BOLD_FONT = font.Font(**{**DEFAULT_FONT.actual(), 'weight': 'bold', 'underline': 1})

frame1 = ttk.Frame(root, padding=(10, 10, 10, 0))
frame1.pack()

selected_option_var = tk.StringVar(value='profit_rate')
selected_option_var.trace_add("write", on_radiobutton_click)
profit_rate_radio = ttk.Radiobutton(frame1, text='수익률', variable=selected_option_var, value='profit_rate',
                                    style='Toolbutton')
profit_rate_radio.grid(row=0, column=1)
sell_price_radio = ttk.Radiobutton(frame1, text='매도가격', variable=selected_option_var, value='sell_price',
                                   style='Toolbutton')
sell_price_radio.grid(row=0, column=2)

frame2 = ttk.Frame(root, padding=10)
frame2.pack()

buy_price_label = ttk.Label(frame2, text='매수가격')
buy_price_label.configure(font=BOLD_FONT)
buy_price_label.grid(row=0, column=0, sticky='e')
buy_price_entry = ttk.Entry(frame2)
buy_price_entry.grid(row=0, column=1)
buy_price_entry.bind('<KeyRelease>', lambda e: on_key_release(buy_price_entry))

sell_price_label = ttk.Label(frame2, text='매도가격')
sell_price_label.configure(font=BOLD_FONT)
sell_price_label.grid(row=1, column=0, sticky='e')
sell_price_entry = ttk.Entry(frame2)
sell_price_entry.grid(row=1, column=1)
sell_price_entry.bind('<KeyRelease>', lambda e: on_key_release(sell_price_entry))

quantity_label = ttk.Label(frame2, text='수량')
quantity_label.configure(font=BOLD_FONT)
quantity_label.grid(row=2, column=0, sticky='e')
quantity_entry = ttk.Entry(frame2)
quantity_entry.grid(row=2, column=1)
quantity_entry.bind('<KeyRelease>', lambda e: on_key_release(quantity_entry))

profit_rate_label = ttk.Label(frame2, text='수익률(%)')
profit_rate_label.configure(font=DEFAULT_FONT)
profit_rate_label.grid(row=3, column=0, sticky='e')
profit_rate_entry = ttk.Entry(frame2)
profit_rate_entry.grid(row=3, column=1)
profit_rate_entry.bind('<KeyRelease>', lambda e: on_key_release(profit_rate_entry))

profit_label = ttk.Label(frame2, text='수익(원)')
profit_label.grid(row=4, column=0, sticky='e')
profit_entry = ttk.Entry(frame2)
profit_entry.grid(row=4, column=1)

fee_label = ttk.Label(frame2, text='수수료 및 세금(원)')
fee_label.grid(row=5, column=0, sticky='e')
fee_entry = ttk.Entry(frame2)
fee_entry.grid(row=5, column=1)

buy_fee_label = ttk.Label(frame2, text='매수 수수료(%)')
buy_fee_label.grid(row=6, column=0, sticky='e')
buy_fee_entry = ttk.Entry(frame2)
buy_fee_entry.grid(row=6, column=1)
buy_fee_entry.insert(0, str(BUY_FEE))
buy_fee_entry.bind('<KeyRelease>', calculate_profit)

sell_fee_label = ttk.Label(frame2, text='매도 수수료(%)')
sell_fee_label.grid(row=7, column=0, sticky='e')
sell_fee_entry = ttk.Entry(frame2)
sell_fee_entry.grid(row=7, column=1)
sell_fee_entry.insert(0, str(SELL_FEE))
sell_fee_entry.bind('<KeyRelease>', calculate_profit)

tax_label = ttk.Label(frame2, text='증권거래세(%)')
tax_label.grid(row=8, column=0, sticky='e')
tax_entry = ttk.Entry(frame2)
tax_entry.grid(row=8, column=1)
tax_entry.insert(0, str(TAX))
tax_entry.bind('<KeyRelease>', calculate_profit)

frame3 = ttk.Frame(root, padding=(10, 0, 10, 10))
frame3.pack()
always_on_top_var = tk.BooleanVar(value=True)
always_on_top_checkbox = ttk.Checkbutton(frame3, text='항상 위', variable=always_on_top_var, command=toggle_always_on_top)
always_on_top_checkbox.grid(row=9, column=0, sticky='w')

root.mainloop()

 

개선할 점.. 

Configparser나 yaml 등을 이용해 
BUY_FEE = 0.015
SELL_FEE = 0.015
TAX = 0.15
이런 변수들을 외부에서 다룰 수 있도록 한다면 좋을 것 같습니다.

어렵지 않으니 직접 해보시는 것을 추천합니다. 

반응형