Skip to content

Commit

Permalink
Added FEE Action and added notes field in trade (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
ilcardella committed Oct 19, 2019
1 parent 60a9abf commit 45ff21f
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 80 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Issue37 - Improved installation process and dependencies setup
- Updated default .credentials configured path

### Added
- Added Pipfile to manage python dependencies
- Added `FEE` action
- Added `notes` field in trade

## [1.0.0] 2019-05-03
### Added
Expand Down
4 changes: 3 additions & 1 deletion src/Model/Portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ def reload(self, trades_list):
del self._holdings[trade.symbol]
profit = ((trade.price/100) * trade.quantity) - trade.fee
self._cash_available += profit
elif trade.action == Actions.FEE:
self._cash_available -= trade.quantity
self.price_getter.set_symbol_list(self.get_holding_symbols())
for symbol in self._holdings.keys():
self._holdings[symbol].set_open_price(self.compute_avg_holding_open_price(symbol, trades_list))
Expand Down Expand Up @@ -234,7 +236,7 @@ def is_trade_valid(self, newTrade):
"""
Validate the new Trade request against the current Portfolio
"""
if newTrade.action == Actions.WITHDRAW:
if newTrade.action == Actions.WITHDRAW or newTrade.action == Actions.FEE:
if newTrade.quantity > self.get_cash_available():
logging.warning(Messages.INSUF_FUNDING.value)
raise RuntimeError(Messages.INSUF_FUNDING.value)
Expand Down
2 changes: 1 addition & 1 deletion src/TradingMate.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
sys.path.insert(0, parentdir)

from Model.DatabaseHandler import DatabaseHandler
from Utils.Utils import Callbacks, Actions, Messages
from Utils.Utils import Callbacks
from UI.View import View
from Model.Portfolio import Portfolio
from Utils.ConfigurationManager import ConfigurationManager
Expand Down
47 changes: 42 additions & 5 deletions src/UI/AddTradeDialogWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, master, confirmCallback):
self.confirmCallback = confirmCallback
self.transient(self.master)
self.title("Add Trade")
self.geometry("+%d+%d" % (self.master.winfo_rootx()+400, self.master.winfo_rooty()+100))
self.geometry("+{}+{}".format(self.master.winfo_rootx()+400, self.master.winfo_rooty()+100))
self.protocol("WM_DELETE_WINDOW", self.destroy)
self.grab_set()
self.focus_set()
Expand All @@ -39,6 +39,7 @@ def create_UI(self):
ttk.Label(self, text="Price [p] :").grid(row=5, sticky="w", padx=5, pady=5)
ttk.Label(self, text="Fee [£] :").grid(row=6, sticky="w", padx=5, pady=5)
ttk.Label(self, text="Stamp Duty [%] :").grid(row=7, sticky="w", padx=5, pady=5)
ttk.Label(self, text="Notes:").grid(row=8, sticky="w", padx=5, pady=5)

# Define the date entry widget
self.dateSelected = tk.StringVar()
Expand Down Expand Up @@ -84,10 +85,15 @@ def create_UI(self):
self.eStampDuty = ttk.Entry(self, textvariable=self.stampDutySelected)
self.eStampDuty.grid(row=7, column=1, sticky="w", padx=5, pady=5)

self.notesSelected = tk.StringVar()
self.notesSelected.trace_add('write', self.check_data_validity)
self.eNotes = ttk.Entry(self, textvariable=self.notesSelected)
self.eNotes.grid(row=8, column=1, sticky="w", padx=5, pady=5)

cancelButton = ttk.Button(self, text="Cancel", command=self.destroy)
cancelButton.grid(row=8, column=0, sticky="e", padx=5, pady=5)
cancelButton.grid(row=9, column=0, sticky="e", padx=5, pady=5)
self.addButton = ttk.Button(self, text="Add", command=self.add_new_trade)
self.addButton.grid(row=8, column=1, sticky="e", padx=5, pady=5)
self.addButton.grid(row=9, column=1, sticky="e", padx=5, pady=5)
self.addButton.config(state="disabled")

# Make the mas ter thread block execution until this window is closed
Expand All @@ -103,6 +109,7 @@ def on_action_selected(self, selection):
self.priceSelected.set("")
self.feeSelected.set("")
self.stampDutySelected.set("")
self.notesSelected.set("")
# Change layout
if selection == Actions.BUY.name:
self.eMarket.config(state='enabled')
Expand All @@ -111,34 +118,47 @@ def on_action_selected(self, selection):
self.ePrice.config(state='enabled')
self.eFee.config(state='enabled')
self.eStampDuty.config(state='enabled')
self.eNotes.config(state='enabled')
elif selection == Actions.SELL.name:
self.eMarket.config(state='enabled')
self.eSymbol.config(state='enabled')
self.e_quantity.config(state='enabled')
self.ePrice.config(state='enabled')
self.eFee.config(state='enabled')
self.eStampDuty.config(state='disabled')
self.eNotes.config(state='enabled')
elif selection == Actions.DEPOSIT.name:
self.eMarket.config(state='disabled')
self.eSymbol.config(state='disabled')
self.e_quantity.config(state='enabled')
self.ePrice.config(state='disabled')
self.eFee.config(state='disabled')
self.eStampDuty.config(state='disabled')
self.eNotes.config(state='enabled')
elif selection == Actions.DIVIDEND.name:
self.eMarket.config(state='enabled')
self.eSymbol.config(state='enabled')
self.e_quantity.config(state='enabled')
self.ePrice.config(state='disabled')
self.eFee.config(state='disabled')
self.eStampDuty.config(state='disabled')
self.eNotes.config(state='enabled')
elif selection == Actions.WITHDRAW.name:
self.eMarket.config(state='disabled')
self.eSymbol.config(state='disabled')
self.e_quantity.config(state='enabled')
self.ePrice.config(state='disabled')
self.eFee.config(state='disabled')
self.eStampDuty.config(state='disabled')
self.eNotes.config(state='enabled')
elif selection == Actions.FEE.name:
self.eMarket.config(state='disabled')
self.eSymbol.config(state='disabled')
self.e_quantity.config(state='enabled')
self.ePrice.config(state='disabled')
self.eFee.config(state='disabled')
self.eStampDuty.config(state='disabled')
self.eNotes.config(state='enabled')

def add_new_trade(self):
# Get selected data and call callback
Expand All @@ -151,6 +171,7 @@ def add_new_trade(self):
item["price"] = float(self.priceSelected.get()) if self.priceSelected.get() is not "" else 0
item["fee"] = float(self.feeSelected.get()) if self.feeSelected.get() is not "" else 0
item["stamp_duty"] = float(self.stampDutySelected.get()) if self.stampDutySelected.get() is not "" else 0
item["notes"] = str(self.notesSelected.get())
newTrade = Trade.from_dict(item)
result = self.confirmCallback(newTrade)

Expand All @@ -161,8 +182,13 @@ def add_new_trade(self):

def check_data_validity(self, *args):
# Check the validity of the Entry widgets data to enable the Add button
valid = self.is_date_valid() and self.is_symbol_valid() and self.is_quantity_valid() \
and self.is_price_valid() and self.is_fee_valid() and self.is_sd_valid()
valid = self.is_date_valid() \
and self.is_symbol_valid() \
and self.is_quantity_valid() \
and self.is_price_valid() \
and self.is_fee_valid() \
and self.is_sd_valid() \
and self.is_notes_valid()
self.addButton.config(state="normal" if valid else "disabled")

def is_date_valid(self):
Expand Down Expand Up @@ -230,3 +256,14 @@ def is_sd_valid(self):
return False
except Exception:
return False

def is_notes_valid(self):
# If widget is disabled it should not affect the overall validity
if str(self.eNotes.cget("state")) == tk.DISABLED:
return True
try:
test = str(self.notesSelected.get())
except Exception as e:
return False
return True

2 changes: 0 additions & 2 deletions src/UI/SettingsWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
parentdir = os.path.dirname(currentdir)
sys.path.insert(0,parentdir)

from Utils.Utils import Actions, Markets
from Utils.Trade import Trade
from .WarningWindow import WarningWindow

class SettingsWindow(tk.Toplevel):
Expand Down
13 changes: 8 additions & 5 deletions src/Utils/Trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


class Trade():
def __init__(self, date_string, action, quantity, symbol, price, fee, sdr):
def __init__(self, date_string, action, quantity, symbol, price, fee, sdr, notes):
try:
self.date = datetime.datetime.strptime(date_string, '%d/%m/%Y')
if not isinstance(action, Actions):
Expand All @@ -25,6 +25,7 @@ def __init__(self, date_string, action, quantity, symbol, price, fee, sdr):
self.price = price
self.fee = fee
self.sdr = sdr
self.notes = notes
self.total = self.__compute_total()
except Exception as e:
logging.error(e)
Expand All @@ -38,19 +39,21 @@ def to_dict(self):
'symbol': self.symbol,
'price': self.price,
'fee': self.fee,
'stamp_duty': self.sdr
'stamp_duty': self.sdr,
'notes': self.notes
}

@staticmethod
def from_dict(item):
if any(['date' not in item, 'action' not in item, 'quantity' not in item, 'symbol' not in item, 'price' not in item, 'fee' not in item, 'stamp_duty' not in item]):
if any(['date' not in item, 'action' not in item, 'quantity' not in item, 'symbol' not in item, 'price' not in item, 'fee' not in item, 'stamp_duty' not in item, 'notes' not in item]):
raise ValueError('item not well formatted')

return Trade(item['date'], Actions[item['action']], item['quantity'],
item['symbol'], float(item['price']), float(item['fee']), float(item['stamp_duty']))
item['symbol'], float(item['price']), float(item['fee']),
float(item['stamp_duty']), str(item['notes']))

def __compute_total(self):
if self.action in (Actions.DEPOSIT, Actions.WITHDRAW, Actions.DIVIDEND):
if self.action in (Actions.DEPOSIT, Actions.WITHDRAW, Actions.DIVIDEND, Actions.FEE):
return self.quantity
elif self.action == Actions.BUY:
cost = (self.price / 100) * self.quantity
Expand Down
1 change: 1 addition & 0 deletions src/Utils/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Actions(Enum):
DEPOSIT = 3
DIVIDEND = 4
WITHDRAW = 5
FEE = 6

class Messages(Enum):
INSUF_FUNDING = "ERROR: Insufficient funding available"
Expand Down

0 comments on commit 45ff21f

Please sign in to comment.