#include "mainwindow.h" #include "ui_mainwindow.h" #include #include void MainWindow::LoadSettings () { QSettings settings("Mobius", "Risk Calculator"); QString current_profile = ui->profile_selector->currentText(); settings.beginGroup("profiles/" + current_profile); ui->TotCap_in->setText(settings.value("TotCapV").toString()); ui->RiskPerc_in->setText(settings.value("RiskPercV").toString()); ui->Crncy_in->setCurrentIndex(settings.value("CrncyV").toInt()); ui->FeeType_in->setCurrentIndex(settings.value("FeeTypeV").toInt()); ui->TFee_in->setText(settings.value("TFeeV").toString()); ui->MFee_in->setText(settings.value("MFeeV").toString()); ui->CFee_in->setText(settings.value("CFeeV").toString()); ui->risktypebox->setCurrentIndex(settings.value("RiskTypeV").toInt()); for (int i = 0; i < ui->RRWidget->rowCount(); ++i) { for (int n = 0; n < 2; ++n) { QString rrshaver = QString::fromStdString("RRShaver_" + std::to_string(i) + std::to_string(n)), load_string = settings.value(rrshaver).toString(); if (load_string == "0") {load_string = "";} ui->RRWidget->item(i, n)->setText(load_string); } } ui->options_g_rsh->setChecked(settings.value("Options_G_Round_0").toBool()); ui->options_g_rsh_ch->setCurrentIndex(settings.value("Options_G_Round_1").toInt()); ui->options_g_rrinfees->setChecked(settings.value("Options_G_RRINFEES").toBool()); settings.endGroup(); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); auto dv = new QDoubleValidator(); ui->TotCap_in->setValidator(dv); ui->RiskPerc_in->setValidator(dv); ui->EntryP_in->setValidator(dv); ui->StopP_in->setValidator(dv); ui->TargP_in->setValidator(dv); ui->TFee_in->setValidator(dv); ui->MFee_in->setValidator(dv); ui->CFee_in->setValidator(dv); init_stage = true; /* Setting the initialization stage flag so that the program knows that it is initializing and doesn't try saving incomplete settings */ calculate_v = false; QSettings settings("Mobius", "Risk Calculator"); settings.beginGroup("profiles"); // When starting the app, iterate through all of the profiles in registry and add them to the profiles selector if (settings.childGroups().count() == 0) { // If there are no profiles/first run, create a default one settings.setValue("Default/init", 0); settings.setValue("Default/FeeTypeV", 1); settings.endGroup(); settings.setValue("CurrentProfileIndex", 0); settings.beginGroup("profiles"); } ui->profile_selector->addItems(settings.childGroups()); settings.endGroup(); int current_profile_index = settings.value("CurrentProfileIndex").toInt(); // Set the current profile selector index to the one set in the registry ui->profile_selector->setCurrentIndex(current_profile_index); ui->options_g_mwsot->setChecked(settings.value("Options_G_MWSOT").toBool()); restoreGeometry(settings.value("WindowGeometry", geometry()).toByteArray()); restoreState(settings.value("WindowState").toByteArray()); LoadSettings(); if (ui->options_g_mwsot->isChecked() == true) { setWindowFlags(Qt::WindowStaysOnTopHint); } calculate_v = true; init_stage = false; } MainWindow::~MainWindow() { QSettings settings("Mobius", "Risk Calculator"); settings.setValue("WindowGeometry", saveGeometry()); settings.setValue("WindowState", saveState()); delete ui; } void MainWindow::SaveSettings() { QSettings settings("Mobius", "Risk Calculator"); QString current_profile = ui->profile_selector->currentText(); int current_profile_index = ui->profile_selector->currentIndex(); settings.setValue("CurrentProfile", current_profile); settings.setValue("CurrentProfileIndex", current_profile_index); settings.beginGroup("profiles/" + current_profile); settings.setValue("TotCapV", ui->TotCap_in->text()); settings.setValue("RiskPercV", ui->RiskPerc_in->text()); settings.setValue("CrncyV", ui->Crncy_in->currentIndex()); settings.setValue("FeeTypeV", ui->FeeType_in->currentIndex()); settings.setValue("TFeeV", ui->TFee_in->text()); settings.setValue("MFeeV", ui->MFee_in->text()); settings.setValue("CFeeV", ui->CFee_in->text()); settings.setValue("RiskTypeV", ui->risktypebox->currentIndex()); for (int i = 0; i < ui->RRWidget->rowCount(); ++i) { for (int n = 0; n < 2; ++n) { QString rrshaver = QString::fromStdString("RRShaver_" + std::to_string(i) + std::to_string(n)); settings.setValue(rrshaver, ui->RRWidget->item(i, n)->text().toDouble()); } } settings.setValue("Options_G_Round_0", ui->options_g_rsh->isChecked()); settings.setValue("Options_G_Round_1", ui->options_g_rsh_ch->currentIndex()); settings.setValue("Options_G_MWSOT", ui->options_g_mwsot->isChecked()); settings.setValue("Options_G_RRINFEES", ui->options_g_rrinfees->isChecked()); settings.endGroup(); } void MainWindow::new_profile(QString profile_name) { QSettings settings("Mobius", "Risk Calculator"); settings.beginGroup("profiles"); settings.setValue(profile_name + "/init", 0); settings.setValue(profile_name + "/TotCapV", ui->TotCap_in->text()); settings.setValue(profile_name + "/RiskPercV", ui->RiskPerc_in->text()); settings.setValue(profile_name + "/CrncyV", ui->Crncy_in->currentIndex()); settings.setValue(profile_name + "/FeeTypeV", ui->FeeType_in->currentIndex()); settings.setValue(profile_name + "/TFeeV", ui->TFee_in->text()); settings.setValue(profile_name + "/MFeeV", ui->MFee_in->text()); settings.setValue(profile_name + "/CFeeV", ui->CFee_in->text()); settings.setValue(profile_name + "/RiskTypeV", ui->risktypebox->currentIndex()); for (int i = 0; i < ui->RRWidget->rowCount(); ++i) { for (int n = 0; n < 2; ++n) { QString rrshaver = QString::fromStdString("RRShaver_" + std::to_string(i) + std::to_string(n)); settings.setValue(profile_name + "/" + rrshaver, ui->RRWidget->item(i, n)->text().toDouble()); } } settings.setValue(profile_name + "/Options_G_Round_0", ui->options_g_rsh->isChecked()); settings.setValue(profile_name + "/Options_G_Round_1", ui->options_g_rsh_ch->currentIndex()); settings.setValue(profile_name + "/Options_G_RRINFEES", ui->options_g_rrinfees->isChecked()); settings.endGroup(); } void MainWindow::calculate() { int FeeType = ui->FeeType_in->currentIndex(); double TotCap = ui->TotCap_in->text().toDouble(); double EntryP = ui->EntryP_in->text().toDouble(); double StopP = ui->StopP_in->text().toDouble(); double TargP = ui->TargP_in->text().toDouble(); double TFee = ui->TFee_in->text().toDouble() / 100; double MFee = ui->MFee_in->text().toDouble() / 100; double CFee = ui->CFee_in->text().toDouble(); double PosSize = 0, Units = 0, PosSizeRaw = 0, Fees = 0, FeesRaw = 0, Levg = 0, AmtRisk = 0, PercRiskd = 0, Profit = 0.0, ARR = 0 , RiskPerc = 0 , PosDivider = 0; if (ui->risktypebox->currentIndex() == 0) { RiskPerc = ui->RiskPerc_in->text().toDouble() / 100; } else { if (ui->Crncy_in->currentIndex() == 0) { RiskPerc = ui->RiskPerc_in->text().toDouble() / (TotCap * EntryP); } else { RiskPerc = ui->RiskPerc_in->text().toDouble() / TotCap; } } if (ui->Crncy_in->currentIndex() == 0) { TotCap *= EntryP; } bool Long = (EntryP > StopP) ? true : false; //--------- Raw Position Size Calculation Block AmtRisk = TotCap * RiskPerc; PosDivider = (abs(EntryP - StopP)) / EntryP; PosSizeRaw = (AmtRisk / PosDivider); //--------- //--------- Raw Fee Calculation Block switch(FeeType) { case 1: // Taker FeesRaw = PosSizeRaw * (TFee * 2); break; case 2: // Maker FeesRaw = PosSizeRaw * (MFee * 2); break; case 3: // Flat-rate PosSizeRaw = ((AmtRisk - (CFee * 2)) / PosDivider); break; case 5: // TradeStation Global (US Stocks) FeesRaw = (PosSizeRaw / EntryP) * 0.007 * 2; if (FeesRaw < 1.5 * 2) { PosSizeRaw = ((AmtRisk - (1.5 * 2)) / PosDivider); } break; case 6: // TradeZero if ((PosSizeRaw / EntryP) >= 200) { FeesRaw = (PosSizeRaw / EntryP) * 0.005 * 2; } else { PosSizeRaw = ((AmtRisk - (0.99 * 2)) / PosDivider); } break; } //--------- PosSize = floor(PosSizeRaw / ((TotCap * RiskPerc + FeesRaw) / TotCap / RiskPerc)); //--------- Exchange-Defined PosSize Re-Check Block switch(FeeType) { case 5: // TradeStation Global (US Stocks) if (((PosSize / EntryP) * 0.007 * 2) < 1.5 * 2) { //Checks if per-share-fee for the entire position is below 1.5 * 2 PosSizeRaw = ((AmtRisk - (1.5 * 2)) / PosDivider); PosSize = floor(PosSizeRaw / ((TotCap * RiskPerc) / TotCap / RiskPerc)); } break; case 6: // TradeZero if (PosSize / EntryP <= 200 && PosSizeRaw / EntryP >= 200) { PosSizeRaw = ((AmtRisk - (0.99 * 2)) / PosDivider); PosSize = floor(PosSizeRaw / ((TotCap * RiskPerc) / TotCap / RiskPerc)); } break; } //--------- Units = PosSize / EntryP; if (ui->options_g_rsh->isChecked()) { switch (ui->options_g_rsh_ch->currentIndex()) { case 0: Units = ceil(Units); break; case 1: Units = round(Units); break; case 2: Units = floor(Units); break; } } PosSize = Units * EntryP; Levg = PosSize / TotCap; PercRiskd = AmtRisk / TotCap; //--------- Actual Fee Calculation Block switch(FeeType) { case 1: // Taker Fees = PosSize * (TFee * 2); break; case 2: // Maker Fees = PosSize * (MFee * 2); break; case 3: // Flat-rate Fees = CFee * 2; break; case 5: // TradeStation Global (US Stocks) Fees = Units * 0.007 * 2; if (Fees < 1.5 * 2) {Fees = 1.5 * 2;} break; case 6: // TradeZero Fees = (Units >= 200) ? Units * 0.005 * 2 : 0.99 * 2; break; } //--------- if (Long && EntryP != 0 && StopP != 0) { ui->Direction_out->setStyleSheet("background-color: #d8fca8"); ui->Direction_out->setText("LONG"); } else if (EntryP != 0 && StopP != 0) { ui->Direction_out->setStyleSheet("background-color: #ff9b9b"); ui->Direction_out->setText("SHORT"); } Profit = (abs(TargP - EntryP) / EntryP) * PosSize; ARR = Profit / AmtRisk; if (ARR < 0) {ARR = 0;} if (Fees >= AmtRisk) {PosSize = Units = Levg = Profit = 0;} // If fees exceed the wanted risk amount, set everything to 0 if(!std::isnan(PosSize) && StopP != 0) ui->PosSize_out->setText(QString::number(PosSize, 'f', 0)); else ui->PosSize_out->setText("0"); if(!std::isnan(Units) && StopP != 0) { if (std::fmod(Units, 1) != 0) { ui->Units_out->setText(QString::number(Units, 'f', 4)); } else { ui->Units_out->setText(QString::number(Units, 'f', 0)); } } else { ui->Units_out->setText("0"); } if(!std::isnan(Levg)) ui->Levg_out->setText(QString::number(Levg, 'f', 2)); else ui->Levg_out->setText("0.00"); if(!std::isnan(Fees)) ui->Fees_out->setText("$" + QString::number(Fees, 'f', 2)); else ui->Fees_out->setText("$0.00"); if(!std::isnan(AmtRisk)) ui->AmtRisk_out->setText("$" + QString::number(AmtRisk, 'f', 2)); else ui->AmtRisk_out->setText("$0.00"); if(!std::isnan(PercRiskd)) ui->PercRiskd_out->setText(QString::number(PercRiskd * 100, 'f', 2) + "%"); else ui->PercRiskd_out->setText("0.00%"); if(!std::isnan(Profit) && TargP != 0 ) ui->Profit_out->setText("$" + QString::number(Profit, 'f', 2)); else ui->Profit_out->setText("$0.00"); if(!std::isnan(ARR) && TargP != 0) ui->ARR_out->setText(QString::number(ARR, 'f', 2)); else ui->ARR_out->setText("0.00"); if(!std::isnan(TotCap)) ui->USDBal_out->setText("$" + QString::number(TotCap, 'f', 2)); else ui->USDBal_out->setText("$0.00"); if (ui->RiskPerc_in->text() == "0.69") {ui->USDBal_out->setText("nice ( ͡° ͜ʖ ͡°)");} if (ui->RiskPerc_in->text() == "0.420") {ui->USDBal_out->setText("420 Blaze It");} if (ui->RiskPerc_in->text() == "0.369") {ui->USDBal_out->setText("Keys To The Universe");} /* RR Shaver */ std::vector rs_perc, rs_rr; QString shave_units, shave_price; double TotalPercClosed = 0, RealRLocked = 0, RWhenStopped = 0, RLeftToClose = 100; for (int i = 0; i < ui->RRWidget->rowCount(); ++i) { rs_perc.push_back(ui->RRWidget->item(i, 0)->text().toDouble()); // Shave Percentages rs_rr.push_back(ui->RRWidget->item(i, 1)->text().toDouble()); // Risk Shaver } calculate_v = false; for (int i = 0; i < ui->RRWidget->rowCount(); ++i) { double shave_perc = rs_perc[i] / 100, cut_rr = rs_rr[i]; shave_units = QString::number(Units * shave_perc); if (ui->options_g_rsh->isChecked()) { switch (ui->options_g_rsh_ch->currentIndex()) { case 0: shave_units = QString::number(ceil(shave_units.toDouble())); break; case 1: shave_units = QString::number(round(shave_units.toDouble())); break; case 2: shave_units = QString::number(floor(shave_units.toDouble())); break; } } double cut_rr_sp; if (ui->options_g_rrinfees->isChecked()) { cut_rr_sp = cut_rr + (Fees/(AmtRisk - Fees))*cut_rr; } else { cut_rr_sp = cut_rr; } int decim_e = countDecimals(ui->EntryP_in->text().toStdString()); int decim_s = countDecimals(ui->StopP_in->text().toStdString()); int decim = decim_e > decim_s ? decim_e : decim_s; if (Long) { shave_price = QString::number(EntryP + ((PosDivider * EntryP) * cut_rr_sp),'f',decim); } else { shave_price = QString::number(EntryP - ((PosDivider * EntryP) * cut_rr_sp),'f',decim); } if (ui->RRWidget->item(i, 0)->text().toDouble() != 0 && ui->RRWidget->item(i, 1)->text().toDouble() != 0) { RealRLocked += cut_rr * shave_perc; RLeftToClose -= rs_perc[i]; TotalPercClosed += shave_perc; if (ui->PosSize_out->text() != "0") { ui->RRWidget->item(i, 2)->setText(shave_units); ui->RRWidget->item(i, 3)->setText(shave_price); } ui->RRWidget->item(i, 4)->setText(QString::number(RealRLocked) + " R"); } else { ui->RRWidget->item(i, 2)->setText(""); ui->RRWidget->item(i, 3)->setText(""); ui->RRWidget->item(i, 4)->setText(""); } } RWhenStopped = RealRLocked - (1 - TotalPercClosed); if (RLeftToClose < 0) {RLeftToClose = 0;} if (RWhenStopped > RealRLocked) {RWhenStopped = RealRLocked;} ui->rrshaver_trt->setText(QString::number(RealRLocked) + " R"); ui->rrshaver_ltc->setText(QString::number(RLeftToClose) + "%"); ui->rrshaver_rrws->setText(QString::number(RWhenStopped) + " R"); calculate_v = true; if (init_stage != true) {SaveSettings();} } void MainWindow::on_TotCap_in_textEdited() { calculate(); } void MainWindow::on_Crncy_in_currentIndexChanged() { if (calculate_v != false) {calculate();} } void MainWindow::on_RiskPerc_in_textEdited() { calculate(); } void MainWindow::on_EntryP_in_textEdited() { calculate(); } void MainWindow::on_StopP_in_textEdited() { calculate(); } void MainWindow::on_TargP_in_textChanged() { calculate(); } void MainWindow::on_FeeType_in_currentIndexChanged() { if (calculate_v != false) {calculate();} } void MainWindow::on_TFee_in_textEdited() { calculate(); } void MainWindow::on_MFee_in_textEdited() { calculate(); } void MainWindow::on_risktypebox_currentIndexChanged() { if (calculate_v != false) {calculate();} } void MainWindow::on_PosSize_copy_clicked() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ui->PosSize_out->text()); } void MainWindow::on_Units_copy_clicked() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ui->Units_out->text()); } void MainWindow::on_EntryP_copy_clicked() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ui->EntryP_in->text()); } void MainWindow::on_StopP_copy_clicked() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(ui->StopP_in->text()); } void MainWindow::on_CFee_in_textEdited() { calculate(); } void MainWindow::on_pushButton_clicked() { QDesktopServices::openUrl(QUrl("https://kazonomics.com")); } void MainWindow::on_RRWidget_itemChanged() { if (calculate_v != false) {calculate();} } void MainWindow::on_options_rrsh_1_stateChanged() { calculate(); } void MainWindow::on_options_g_rsh_stateChanged() { if (ui->options_g_rsh->isChecked()) { ui->options_g_rsh_ch->setEnabled(1); } else { ui->options_g_rsh_ch->setEnabled(0); } if (calculate_v != false) {calculate();} } void MainWindow::on_options_g_rsh_ch_currentIndexChanged() { if (calculate_v != false) {calculate();} } void MainWindow::on_pushButton_2_clicked() { QMessageBox *about = new QMessageBox(this); about->setWindowTitle("Mobius Risk Calculator | About"); about->setTextFormat(Qt::RichText); about->setIconPixmap(QPixmap(":/icon/app-png.png")); about->setText("Mobius Risk Calculator
Version 2.5 (build 1313)"); about->setInformativeText(QByteArray::fromBase64("Q29weXJpZ2h0IMKpIDIwMjEgUmVkZWN0cm88YnI+PGJyPkUtbWFpbDogPGEgaHJlZj0nbWFpbHRvOnJlZEBsaXRlc2VjLmNvJz5yZWRAbGl0ZXNlYy5jbzwvYT48YnI+VHdpdHRlcjogPGEgaHJlZj0naHR0cDovL3R3aXR0ZXIuY29tL01vYml1c1JlZCc+QE1vYml1c1JlZDwvYT48aHI+PGNlbnRlcj5FeGNsdXNpdmUgcHJvZHVjdCBmb3IgdGhlPGJyPkthem9ub21pY3MgdHJhZGluZyBlY29zeXN0ZW06PGJyPjxhIGhyZWY9J2h0dHA6Ly9rYXpvbm9taWNzLmNvbSc+V2Vic2l0ZTwvYT4gPGEgaHJlZj0naHR0cDovL3dpbnRlcm5vbWljcy50dic+VHdpdGNoPC9hPiA8YSBocmVmPSdodHRwOi8vb25seWZhbnMuY29tL0them9ub21pY3MnPk9ubHlGYW5zPC9hPjwvY2VudGVyPg==")); about->exec(); } void MainWindow::on_profile_selector_currentIndexChanged() { calculate_v = false; if (init_stage != true) {LoadSettings();} calculate_v = true; if (init_stage != true) {calculate();} } void MainWindow::on_delete_button_clicked() { QMessageBox::StandardButton confirmation; confirmation = QMessageBox::question(this, "Delete profile?", "Do you want to delete the profile \"" + ui->profile_selector->currentText() + "\"?", QMessageBox::Yes|QMessageBox::No); if (confirmation == QMessageBox::Yes) { QSettings settings("Mobius", "Risk Calculator"); //Deletes the profile selector settings.beginGroup("profiles"); if (settings.childGroups().count() == 1) { QMessageBox::critical(this, "Error", "Can't delete all profiles! At least one is needed to operate the calculator"); } else { settings.remove(ui->profile_selector->currentText()); init_stage = true; ui->profile_selector->clear(); ui->profile_selector->addItems(settings.childGroups()); ui->profile_selector->setCurrentIndex(settings.value("CurrentProfileIndex").toInt()); settings.endGroup(); LoadSettings(); init_stage = false; calculate(); } } } void MainWindow::on_new_button_clicked() { bool ok; QString newProfileName = QInputDialog::getText(this,"New profile","Enter name for the new profile:", QLineEdit::Normal, "", &ok).trimmed(); QSettings settings("Mobius", "Risk Calculator"); if (ok && newProfileName == "") { QMessageBox::critical(this, "Error", "Profile name can't be empty!"); } else if (ok && settings.value("profiles/" + newProfileName + "/init").toString() != "") { QMessageBox::critical(this, "Error", "Profile named \"" + newProfileName + "\" already exists!"); } else { // Adds the New Group if (ok) { new_profile(newProfileName); ui->profile_selector->addItem(newProfileName); ui->profile_selector->model()->sort(0); } } } void MainWindow::on_rename_button_clicked() { bool ok; QString newProfileName = QInputDialog::getText(this,"Rename profile","Enter a new name for the profile \"" + ui->profile_selector->currentText() +"\":", QLineEdit::Normal, "", &ok).trimmed(); QSettings settings("Mobius", "Risk Calculator"); if (ok && newProfileName == "") { QMessageBox::critical(this, "Error", "Profile name can't be empty!"); } else { if (ok && newProfileName != ui->profile_selector->currentText()) { new_profile(newProfileName); settings.beginGroup("profiles"); settings.remove(ui->profile_selector->currentText()); init_stage = true; ui->profile_selector->clear(); ui->profile_selector->addItems(settings.childGroups()); init_stage = false; ui->profile_selector->setCurrentIndex(ui->profile_selector->findText(newProfileName, Qt::MatchContains)); settings.endGroup(); } } } void MainWindow::on_options_g_mwsot_stateChanged() { if (init_stage != true) { QMessageBox::information(this, "Info", "Changes will be in effect on the next launch of the program."); SaveSettings(); } } void MainWindow::on_options_g_rrinfees_stateChanged() { if (calculate_v != false) {calculate();} } int MainWindow::countDecimals(std::string a) { if (a.find(".") != std::string::npos && a.back() != '.') { return a.size() - a.find_last_of('.') - 1; } else { return 0; } }