Major Update

This commit is contained in:
2025-11-17 23:58:58 +01:00
parent 1392a27b7d
commit fe86a3d71f
7 changed files with 976462 additions and 3286 deletions

150
main.py
View File

@@ -41,6 +41,7 @@ test_data = '''{
trmnl_object = '''{"merge_variables": {"key": "payload"}}'''
### -------------------- EXAMPLE STRUCTURES --------------------
# NOTION FORMATED DATA STRUCTURE
''' trades[notion_page_id] = {
@@ -76,6 +77,7 @@ Date Close Dividends
'''var data = [{"name":"Current","data":[["2024-12-31",3982.23],......,["2024-12-01",946.02]]},{"name":"Comparison","data":[["2024-12-30",590.56],......,["2024-12-01",425.28]]}];'''
### -------------------- FUNCTIONS --------------------
# ------------------#
# LEVEL 1 FUNCTIONS #
@@ -84,11 +86,18 @@ Date Close Dividends
def calculate_irr(date_now, date_open, value_now, value_open):
error = False
irr = 0.0
try:
# Prepare IRR calculation
# Count the number in days
a = date_now - date_open
a = a.days
a = a / 365 # Umrechnung von Tagesrendite auf Jahresrendite
# Am Tag des Kaufs selbst, liegt das Delta in Tagen bei 0
# Um dennoch einen IRR kalkulieren zu können, wird das Delta auf 1 gsetzt
if a == 0:
a = 1
a = a / 365 # Umrechnung auf Jahresanteil, um auch den Jahreszinssatz zu bekommen
b = value_now / value_open
# Catch negative IRRs
@@ -218,6 +227,7 @@ def push_trmnl_update_chart(wklydict_numbers, dict_chart, trmnl_page_id):
'''
# ------------------#
# LEVEL 2 FUNCTIONS #
# ------------------#
@@ -253,13 +263,7 @@ def fetch_format_notion_data(db_id_trades):
# Each page is loaded as a dictionary
notion_page = dict(i)
# Handling missing entry
try:
date_open = notion_page["properties"]["Open"]["date"]
date_open = date_open["start"]
date_open = datetime.date(*map(int, date_open.split('-')))
except:
date_open = 0
# Handling desired missing entries
try:
date_close = notion_page["properties"]["Close"]["date"]
date_close = date_close["start"]
@@ -267,21 +271,35 @@ def fetch_format_notion_data(db_id_trades):
except:
date_close = 0
# Save values
notion_page_id = notion_page["id"] # Use as key for the dictionary
trade = {}
trade = {
'ticker' : notion_page["properties"]["Ticker"]["select"]["name"],
'date_open' : date_open,
'date_close' : date_close,
'course_open' : notion_page["properties"]["Open (€)"]["number"],
'course_close' : notion_page["properties"]["Close (€)"]["number"],
'course_current' : notion_page["properties"]["Current (€)"]["number"],
'irr' : notion_page["properties"]["IRR (%)"]["number"],
'units' : notion_page["properties"]["Units"]["number"],
'dividends' : notion_page["properties"]["Dividends (€)"]["number"]
}
trades[notion_page_id] = trade
# Handeling non-desired missing entries (by skipping this trade)
try:
# Try extracting values
trade = {}
# Format date-open
date_open = notion_page["properties"]["Open"]["date"]
date_open = date_open["start"]
date_open = datetime.date(*map(int, date_open.split('-')))
# Combine data into json structure
trade = {
'ticker' : notion_page["properties"]["Ticker"]["select"]["name"],
'date_open' : date_open,
'date_close' : date_close,
'course_open' : notion_page["properties"]["Open (€)"]["number"],
'course_close' : notion_page["properties"]["Close (€)"]["number"],
'course_current' : notion_page["properties"]["Current (€)"]["number"],
'irr' : notion_page["properties"]["IRR (%)"]["number"],
'units' : notion_page["properties"]["Units"]["number"],
'dividends' : notion_page["properties"]["Dividends (€)"]["number"]
}
# Save values
notion_page_id = notion_page["id"] # Use as key for the dictionary
trades[notion_page_id] = trade
except:
print("[ERROR] Skipped an entry in the notion trades-db - Missing values?")
# Return data if successful
if error == True:
@@ -323,7 +341,7 @@ def fetch_format_yf_data(tickers):
new_index = []
x = 0
while x < data_rows:
date = pd.Timestamp.date(old_index[x]) # Converts the "Pandas Timestamp"-object to a "date" object
date = pd.Timestamp.date(old_index[x]) # Converts the "Pandas Timestamp"-object to a "date" object
new_index.append(date)
x+=1
@@ -332,7 +350,7 @@ def fetch_format_yf_data(tickers):
data.set_index('Date', inplace=True)
# Save the data-frame to the yf_data dict
yf_data[i] = data
yf_data[ticker] = data
# Wait for the API to cool down
time.sleep(t_sleep_api)
@@ -341,19 +359,28 @@ def fetch_format_yf_data(tickers):
error = True
print("[ERROR] Fetching data from yahoo-finance for ticker: {}".format(ticker))
# Create file for easier development
data.to_csv('yf_data_{}.csv'.format(ticker), index=False)
with open("yf_data.txt", "w") as f:
f.write(str(yf_data))
# Return data if successful
if error == True:
return error
else:
return yf_data
# UPDATE NOTION-TRADES-DATABASE
def push_notion_trades_update(trades):
error = False
for notion_page_id in trades:
try:
# The irr is stored in the format 1.2534
# Notion need the format 0,2534
irr_notion = trades[notion_page_id]['irr'] - 1
irr_notion = round(irr_notion, 4)
# Construct Notion-Update-Object
irr_notion = round(trades[notion_page_id]['irr']/100, 4) # Covert to decimal-number for notion
notion_update = {
"Current (€)": {
"number": trades[notion_page_id]['course_current']
@@ -380,24 +407,29 @@ def push_notion_trades_update(trades):
# CALC CURRENT VALUES PER TRADE
def calc_current_value_per_trade(trades, history_per_trade):
# Loop over all trades
for trade_id in trades:
# Determine, what values to fetch based on whether the trade was closed already
date_closed = trades[trade_id]["date_close"]
if date_closed == 0:
# If trade still open, use performance data from today
index_date_iso = datetime.date.today().isoformat()
else:
# If trade closed, use performance data from close-date
index_date_iso = date_closed.isoformat()
# Fetch data from history and save for this trade
trades[trade_id]["course_current"] = history_per_trade[index_date_iso][trade_id]['current_course']
trades[trade_id]["irr"] = history_per_trade[index_date_iso][trade_id]['current_irr']
trades[trade_id]["dividends"] = history_per_trade[index_date_iso][trade_id]['total_dividends']
print ("[SUCCESS] Calculating current value per trade")
return trades
# ------------------#
# LEVEL 3 FUNCTIONS #
# ------------------#
@@ -509,8 +541,9 @@ def calc_history_per_trade(trades, yf_data):
# Fetch course for the day & eventual dividends from yf_data
try:
current_course = yf_data[trade_id].at[index_date, 'Close']
current_dividends_per_ticker = yf_data[trade_id].at[index_date, 'Dividends']
current_course = yf_data[ticker].at[index_date, 'Close']
current_dividends_per_ticker = yf_data[ticker].at[index_date, 'Dividends']
# Catch missing yf-data (eg. for weekends) by reusing course from previous day
except:
current_course = previous_course
@@ -522,18 +555,27 @@ def calc_history_per_trade(trades, yf_data):
if date_close == index_date:
current_course = trades[trade_id]['course_close']
# Save the result for the next iteration
# This setup also makes it possible, that a previous course is passed down across mutiple days
# This makes sense is case i.e. for a weekend
previous_course = current_course
# ------------------ CALCULATE PERFORMANCE IF REQUIRED
if date_open >= index_date and date_close <= index_date:
if index_date >= date_open and index_date <= date_close:
# Calculate performance values
current_amount = trades[trade_id]['units']
current_invested = current_amount * trades[trade_id]['course_open']
total_dividends = total_dividends + current_amount * current_dividends_per_ticker
current_value = current_amount * current_course
current_irr = calculate_irr(index_date, date_open, current_value, current_invested)
total_performanance = current_value - current_invested
current_value_with_dividends = current_value + total_dividends
current_irr = calculate_irr(index_date, date_open, current_value_with_dividends, current_invested)
total_performanance = current_value_with_dividends - current_invested
if current_value_with_dividends == 0:
print("0-value Error with ticker: {}").format(ticker)
else:
# Write 0, if trade is not relevant for current timeframe
current_course = 0.0
current_amount = 0
current_invested = 0.00
total_dividends = 0.00
@@ -569,24 +611,28 @@ def calc_history_per_trade(trades, yf_data):
index_date = index_date + datetime.timedelta(days=1)
# ------------------ LOGGING
print("[SUCCESS] Calculating history for trade: {} with ticker: {}".format(trade_id, ticker))
# ------------------ LOGGING
if warning_count > 0:
print("[WARNING] Calculating history per trade:")
print("[WARNING] No yf-data available in {} cases of ticker & date".format(warning_count))
print("[WARNING] Probably reason is non-trading-days eg. weekends")
print("[WARNING] Used values from previous trade-day instead")
if warning_count > 0:
print("[WARNING] Calculating history for trade: {} with ticker: {}".format(trade_id, ticker))
print(" No yf-data available in {} cases of ticker & date".format(warning_count))
print(" Probably reason is non-trading-days eg. weekends")
print(" Used values from previous trade-day instead")
else:
print("[SUCCESS] Calculating history for trade: {} with ticker: {}".format(trade_id, ticker))
# Reset warning count for the next trade
warning_count = 0
data = json.dumps(history_per_trade, indent=2) # Converts a python-dictionary into a json
# Create file for easier development
# Logging
print("[SUCCESS] Calculating history for trades")
with open("history_per_trade.json", "w") as f:
f.write(data)
return history_per_trade
### -------------------- MAIN PROGRAMM --------------------
while True:
# Clear variables
@@ -596,12 +642,14 @@ while True:
tickers = []
error = False
# Execute Functions
# Fetch Data from the "trades" db in notion
# Contains input fields (i.e. date_open) as well as output fields (i.e. irr, close current)
trades = fetch_format_notion_data(notion_db_id_trades)
if trades == True:
error = True
if error == False:
# Generates a list with unique tickers and no duplicates to reduce workload for the yfinance api
tickers = filter_list_of_tickers(trades)
if tickers == True:
error = True
@@ -612,17 +660,27 @@ while True:
error = True
if error == False:
# Calculates & stores a history per trade indexed by the notion database_id
# Errors in calculating the history should not stop the remaining steps
history_per_trade = calc_history_per_trade(trades, yf_data)
if error == False:
# Selects the most current values for the irr, current course etc. and saves them to the trades-object
# This object stores notion pages with all properties
# By "overwriting" the output properties (i.e. irr) and then overwriting the notion-pages, i can store fresh data
trades = calc_current_value_per_trade(trades, history_per_trade)
if error == False:
list_wkl_dates = create_list_wkl_dates(trades)
# Updates / replaces all pages in the notion "trades"
push_notion_trades_update(trades)
if error == False:
history_total = calc_history_total(history_per_trade, trades)
if error == False:
# Creates a list containing one date per week
list_wkl_dates = create_list_wkl_dates(trades)
if error == False:
history_total_wkl = filter_history_by_list(history_total, list_wkl_dates)