2025-02-10 22:11:57 +00:00
|
|
|
from PyQt6.QtGui import QIntValidator
|
2025-02-10 23:27:22 +00:00
|
|
|
from PyQt6.QtWidgets import QMainWindow, QWidget
|
2025-01-28 15:25:41 +00:00
|
|
|
from PyQt6 import uic
|
2025-01-29 18:24:24 +00:00
|
|
|
|
2025-01-28 15:25:41 +00:00
|
|
|
import output_window
|
|
|
|
|
|
|
|
|
|
|
|
class SIPPCompare(QMainWindow):
|
2025-01-30 15:37:28 +00:00
|
|
|
# Receive instance of PlatformEdit() as parameter
|
2025-02-10 23:27:22 +00:00
|
|
|
def __init__(self, plat_edit_win: QWidget):
|
2025-01-28 15:25:41 +00:00
|
|
|
super().__init__()
|
2025-02-10 22:11:57 +00:00
|
|
|
# Import Qt Designer UI XML file
|
2025-01-28 15:25:41 +00:00
|
|
|
uic.loadUi("gui/main_gui.ui", self)
|
|
|
|
|
2025-01-30 15:37:28 +00:00
|
|
|
# Initialise class variables
|
2025-02-10 22:11:57 +00:00
|
|
|
# Inputs
|
2025-02-11 18:11:54 +00:00
|
|
|
self.optional_boxes = []
|
2025-01-29 18:24:24 +00:00
|
|
|
self.fund_plat_fee = 0.0
|
2025-01-29 20:25:40 +00:00
|
|
|
self.plat_name = ""
|
2025-01-29 18:24:24 +00:00
|
|
|
self.fund_deal_fee = 0.0
|
|
|
|
self.share_plat_fee = 0.0
|
|
|
|
self.share_plat_max_fee = 0.0
|
|
|
|
self.share_deal_fee = 0.0
|
|
|
|
self.share_deal_reduce_trades = 0.0
|
|
|
|
self.share_deal_reduce_amount = 0.0
|
|
|
|
|
2025-02-10 22:11:57 +00:00
|
|
|
# Results
|
2025-01-29 18:24:24 +00:00
|
|
|
self.fund_plat_fees = 0.0
|
|
|
|
self.fund_deal_fees = 0.0
|
|
|
|
self.share_plat_fees = 0.0
|
|
|
|
self.share_deal_fees = 0.0
|
|
|
|
|
2025-01-30 15:37:28 +00:00
|
|
|
# Create window objects
|
|
|
|
self.platform_win = plat_edit_win
|
2025-01-29 18:24:24 +00:00
|
|
|
self.output_win = output_window.OutputWindow()
|
2025-01-28 15:25:41 +00:00
|
|
|
|
|
|
|
# Handle events
|
2025-01-29 18:24:24 +00:00
|
|
|
self.calc_but.clicked.connect(self.calculate_fees)
|
2025-02-10 22:11:57 +00:00
|
|
|
# Menu bar entry (File -> Edit Platforms)
|
2025-01-28 15:25:41 +00:00
|
|
|
self.actionEdit_Platforms.triggered.connect(self.show_platform_edit)
|
2025-02-10 22:11:57 +00:00
|
|
|
# Update percentage mix label when slider moved
|
2025-01-28 15:25:41 +00:00
|
|
|
self.mix_slider.valueChanged.connect(self.update_slider_lab)
|
2025-02-11 19:31:13 +00:00
|
|
|
self.value_input.valueChanged.connect(self.check_valid)
|
2025-02-10 23:27:22 +00:00
|
|
|
self.share_trades_combo.currentTextChanged.connect(self.check_valid)
|
|
|
|
self.fund_trades_combo.currentTextChanged.connect(self.check_valid)
|
2025-01-28 15:25:41 +00:00
|
|
|
|
2025-02-10 22:11:57 +00:00
|
|
|
# Set validators
|
|
|
|
self.share_trades_combo.setValidator(QIntValidator(0, 999))
|
|
|
|
self.fund_trades_combo.setValidator(QIntValidator(0, 99))
|
|
|
|
|
2025-01-28 15:25:41 +00:00
|
|
|
# Display slider position as mix between two nums (funds/shares)
|
|
|
|
def update_slider_lab(self):
|
|
|
|
slider_val = self.mix_slider.value()
|
2025-01-29 18:24:24 +00:00
|
|
|
mix_lab_str = f"Investment mix (funds {slider_val}% / shares {100 - slider_val}%)"
|
|
|
|
self.mix_lab.setText(mix_lab_str)
|
|
|
|
|
2025-02-10 23:27:22 +00:00
|
|
|
def check_valid(self):
|
|
|
|
if self.share_trades_combo.currentText() != "" \
|
2025-02-11 19:31:13 +00:00
|
|
|
and self.fund_trades_combo.currentText() != "" \
|
|
|
|
and self.value_input.value() != 0:
|
2025-02-10 23:27:22 +00:00
|
|
|
self.calc_but.setEnabled(True)
|
|
|
|
else:
|
|
|
|
self.calc_but.setEnabled(False)
|
|
|
|
|
2025-02-10 22:11:57 +00:00
|
|
|
# Get variables from platform editor input fields
|
2025-01-29 18:24:24 +00:00
|
|
|
def init_variables(self):
|
2025-02-11 18:11:54 +00:00
|
|
|
self.optional_boxes = self.platform_win.get_optional_boxes()
|
|
|
|
self.fund_plat_fee = self.platform_win.get_fund_plat_fee()
|
|
|
|
self.fund_deal_fee = self.platform_win.get_fund_deal_fee()
|
|
|
|
self.share_plat_fee = self.platform_win.get_share_plat_fee()
|
|
|
|
self.share_deal_fee = self.platform_win.get_share_deal_fee()
|
|
|
|
|
|
|
|
# TODO: This is HORRIBLE - find better way of doing it! (maybe enums?)
|
|
|
|
if self.optional_boxes[0]:
|
|
|
|
self.plat_name = self.platform_win.get_plat_name()
|
|
|
|
else:
|
|
|
|
self.plat_name = None
|
|
|
|
|
|
|
|
if self.optional_boxes[1]:
|
|
|
|
self.share_plat_max_fee = self.platform_win.get_share_plat_max_fee()
|
|
|
|
else:
|
|
|
|
self.share_plat_max_fee = None
|
|
|
|
|
|
|
|
if self.optional_boxes[2]:
|
|
|
|
self.share_deal_reduce_trades = self.platform_win.get_share_deal_reduce_trades()
|
|
|
|
else:
|
|
|
|
self.share_deal_reduce_trades = None
|
|
|
|
|
|
|
|
if self.optional_boxes[3]:
|
|
|
|
self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount()
|
|
|
|
else:
|
|
|
|
self.share_deal_reduce_amount = None
|
2025-01-28 15:25:41 +00:00
|
|
|
|
|
|
|
# Calculate fees
|
2025-01-29 18:24:24 +00:00
|
|
|
def calculate_fees(self):
|
|
|
|
self.init_variables()
|
2025-02-10 22:11:57 +00:00
|
|
|
# Set to zero each time to avoid persistence
|
2025-01-29 20:25:40 +00:00
|
|
|
self.fund_plat_fees = 0
|
2025-02-10 22:11:57 +00:00
|
|
|
value_num = float(self.value_input.value())
|
|
|
|
# Funds/shares mix
|
2025-02-10 23:27:22 +00:00
|
|
|
slider_val: int = self.mix_slider.value()
|
2025-01-28 15:25:41 +00:00
|
|
|
funds_value = (slider_val / 100) * value_num
|
|
|
|
fund_trades_num = int(self.fund_trades_combo.currentText())
|
2025-01-29 18:24:24 +00:00
|
|
|
self.fund_deal_fees = fund_trades_num * self.fund_deal_fee
|
2025-01-28 15:25:41 +00:00
|
|
|
|
2025-01-29 20:25:40 +00:00
|
|
|
for i in range(1, len(self.fund_plat_fee[0])):
|
|
|
|
band = self.fund_plat_fee[0][i]
|
|
|
|
prev_band = self.fund_plat_fee[0][i - 1]
|
|
|
|
fee = self.fund_plat_fee[1][i]
|
2025-01-28 15:25:41 +00:00
|
|
|
gap = (band - prev_band)
|
|
|
|
|
2025-02-10 22:11:57 +00:00
|
|
|
if funds_value > gap:
|
2025-01-29 18:24:24 +00:00
|
|
|
self.fund_plat_fees += gap * (fee / 100)
|
2025-02-10 22:11:57 +00:00
|
|
|
funds_value -= gap
|
2025-01-28 15:25:41 +00:00
|
|
|
else:
|
2025-02-10 22:11:57 +00:00
|
|
|
self.fund_plat_fees += funds_value * (fee / 100)
|
2025-01-28 15:25:41 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
shares_value = (1 - (slider_val / 100)) * value_num
|
2025-02-17 10:15:58 +00:00
|
|
|
if self.share_plat_max_fee is not None:
|
|
|
|
if (self.share_plat_fee * shares_value / 12) > self.share_plat_max_fee:
|
|
|
|
self.share_plat_fees = self.share_plat_max_fee * 12
|
2025-01-28 15:45:57 +00:00
|
|
|
else:
|
2025-01-29 18:24:24 +00:00
|
|
|
self.share_plat_fees = self.share_plat_fee * shares_value
|
2025-01-28 15:45:57 +00:00
|
|
|
|
2025-01-28 15:25:41 +00:00
|
|
|
share_trades_num = int(self.share_trades_combo.currentText())
|
2025-02-17 10:15:58 +00:00
|
|
|
if self.share_deal_reduce_trades is not None:
|
|
|
|
if (share_trades_num / 12) >= self.share_deal_reduce_trades:
|
|
|
|
self.share_deal_fees = self.share_deal_reduce_amount * share_trades_num
|
2025-01-28 15:45:57 +00:00
|
|
|
else:
|
2025-01-29 18:24:24 +00:00
|
|
|
self.share_deal_fees = self.share_deal_fee * share_trades_num
|
2025-01-28 15:25:41 +00:00
|
|
|
|
2025-01-29 18:24:24 +00:00
|
|
|
self.show_output_win()
|
2025-01-28 15:25:41 +00:00
|
|
|
|
|
|
|
# Show the output window - this func is called from calculate_fee()
|
2025-01-29 18:24:24 +00:00
|
|
|
def show_output_win(self):
|
2025-01-28 15:25:41 +00:00
|
|
|
# Refresh the results when new fees are calculated
|
2025-01-29 18:24:24 +00:00
|
|
|
self.output_win.display_output(self.fund_plat_fees, self.fund_deal_fees,
|
2025-02-10 22:11:57 +00:00
|
|
|
self.share_plat_fees, self.share_deal_fees,
|
|
|
|
self.plat_name
|
|
|
|
)
|
2025-01-28 15:25:41 +00:00
|
|
|
self.output_win.show()
|
|
|
|
|
2025-01-30 15:37:28 +00:00
|
|
|
# Show the platform editor window (currently run-time only)
|
2025-01-28 15:25:41 +00:00
|
|
|
def show_platform_edit(self):
|
|
|
|
self.platform_win.show()
|