restoring from backup

This commit is contained in:
2025-12-25 13:33:03 +01:00
commit 1c671f5213
6 changed files with 1255 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# Bot_Notion_Performance_Tracker
This Bot is intended to fetch stock-data, calculate performance and add key-kpis to a notion-db matching tracker symbols in said notion-db
Current Features:
- Information/KPIs on individual trades-level
- Fetch & format data from a given Notion-table
- Fetch & format historical data from yfinance
- Calculating Dividends
- Calculate the daily IRR (incl. Dividends)
- Pushing updates to Notion
- Dividends ARE calculated correctly...YEEEA!
- Information/KPIs on the whole portfolio
- configuration file
- updating TRML screens (big numbers only)
Current Limitation:
- Additional trackers need to be added to the investments-table manually
Feature Pipeline:
- Secure way of storing secret keys
- Substracting the effect of inflation
- Including TRML as a display target
- Realoading configuration with every iteration

39
config.py Normal file
View File

@@ -0,0 +1,39 @@
### --------- PROGRAMM CONFIGUARTION
programm_cooldown_time = 15 # Programm cooldown timer in minutes
api_cooldowm_time = 0.1 # API cooldown timer in minutes
trmnl_granularity = 70 # Days in between two data points in the TRMNL chart
### Logging
selected_logging_level = "warning" # must be one from the list below
logging_levels = ("none", "error", "success", "warning", "info", "debug") # ordered by amount of logs
class log_colors:
# Code for start of coloring (MUST match logging-levels above)
error = '\033[91m'
warning = '\033[93m'
success = '\033[92m'
info = '\033[90m'
debug = '\033[4m'
# Code for end of coloring
endcode = '\033[0m'
# Fuctionality
update_notion = True
update_TRMNL = True
### --------- API CONFIGURATION
# NOTION
notion_token = "secret_b7PiPL2FqC9QEikqkAEWOht7LmzPMIJMWTzUPWwbw4H"
notion_headers = {
"Authorization": "Bearer " + notion_token,
"Content-Type": "application/json",
"Notion-Version": "2022-02-22"
}
notion_db_id_trades = "95f7a2b697a249d4892d60d855d31bda"
notion_db_id_investments = "2ba10a5f51bd8160ab9ee982bbef8cc3"
notion_db_id_performance = "1c010a5f51bd806f90d8e76a1286cfd4"
# TRMNL
trmnl_headers = {"Content-Type": "application/json"}
trmnl_url_chart_1 = "https://usetrmnl.com/api/custom_plugins/334ea2ed-1f20-459a-bea5-dca2c8cf7714"
trmnl_url_chart_2 = "https://usetrmnl.com/api/custom_plugins/72950759-38df-49eb-99fb-b3e2e67c385e"

42
example data structure.py Normal file
View File

@@ -0,0 +1,42 @@
# NOTION FORMATED DATA STRUCTURE
''' trades[notion_page_id] = {
'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"]
}
'''
# YF FORMATTED DATA STRUCTURE
'''yf_data[notion_page_id] = [1670 rows x 2 columns]
Date Close Dividends
2018-02-14 7.358605 0.0
2018-02-15 7.366426 0.0
2018-02-16 7.366426 0.0
2018-02-19 7.366426 0.0
2018-02-20 7.366426 0.0
... ... ...
2025-06-13 11.024000 0.0
2025-06-16 11.050000 0.0
2025-06-17 11.076000 0.0
2025-06-18 11.098500 0.0
2025-06-19 11.129000 0.0
'''
# TRMNL CHART-DATA STRUCTURE
'''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]]}];'''
# TRMNL Special WebHook Payload Strcuture
# The outer "merge variables" is required to send to data through
test_data = '''{
"merge_variables":
{"key": "data"}
}'''
# TRMNL Diagramm Structure
trmnl_object = '''{"merge_variables": {"key": "payload"}}'''

1031
functions.py Normal file

File diff suppressed because it is too large Load Diff

119
main.py Normal file
View File

@@ -0,0 +1,119 @@
import functions
import config
while True:
# ------------------------------------------- #
# PART 1: Updating the notion trades database #
# ------------------------------------------- #
# Fetches the list of all trades stored in notion
print("Fetching Data from Notion...", end=" ", flush=True)
trades = functions.fetch_format_notion_trades(config.notion_db_id_trades)
# Generates a list with unique tickers and no duplicates to reduce workload for the yfinance api
print("Creating a list of unique tickers...", end=" ", flush=True)
tickers = functions.filter_list_of_tickers(trades)
# Fetches & formats the complete history per ticker from yfinance
print("Fetching & formating yfinance data", end="", flush=True)
yf_data = functions.fetch_format_yf_data(tickers)
# Calculates & stores a history per trade
print("Calculating the history per trade...", end=" ", flush=True)
history_per_trade = functions.calc_history_per_trade(trades, yf_data)
# Configuration dependent execution:
if config.update_notion == True:
# Selects the most current values from the history per trade and overwrites them in the "trades" feteched from notion
print("Selecting the most current values...", end=" ", flush=True)
trades = functions.select_current_value_per_trade(trades, history_per_trade)
# Updates the values in the notion database
print("Updating the notion trades database", end="", flush=True)
functions.push_notion_trades_update(trades)
# ------------------------------------------------ #
# PART 2: Updating the notion investments database #
# ------------------------------------------------ #
# Fetches the list of entries in the investment-overview database stored in notion
print("Fetching & formating notion investments...", end=" ", flush=True)
investments = functions.fetch_format_notion_investments(config.notion_db_id_investments)
# Calculates & stores a history per ticker AND a total across all tickers indexed by the ticker name
print("Calculating history per ticker...", end=" ", flush=True)
history_per_ticker = functions.calc_history_per_ticker(history_per_trade, tickers, trades)
# Configuration dependent execution:
if config.update_notion == True:
# Selects the most current values from the history per ticker and overwrites them in the "investments" feteched from notion
print("Calculating current value per ticker...", end=" ", flush=True)
investments = functions.select_current_value_per_ticker(investments, history_per_ticker)
# Updates the values in the notion database
print("Updating the notion ticker database", end="", flush=True)
functions.push_notion_investment_update(investments)
# --------------------------------- #
# PART 3: Updating the TRMNL Screen #
# --------------------------------- #
# Configuration dependent execution:
if config.update_TRMNL == True:
# Creates a list containing one date per week
print("Creating a list with one entry per week...", end=" ", flush=True)
list_filtered_dates = functions.create_list_filtered_dates(trades, config.trmnl_granularity)
# Filter a weekly snapshot from the history per ticker
print("Filtering the history per ticker to weekly values...", end=" ", flush=True)
history_per_ticker_filtered = functions.filter_history_by_list(history_per_ticker, list_filtered_dates)
# Prepare a new TRMNL update
print("Constructing a TERMNL update object...", end=" ", flush=True)
trmnl_update_object = functions.prep_trmnl_chart_udpate(
history_per_ticker_filtered,
series_to_show_1="total",
data_to_show_1="current_value",
series_to_show_2="DBPG.DE",
data_to_show_2="current_value"
)
# Push the update to TRMNL
print("Updating a TERMNL screen...", end=" ", flush=True)
functions.push_trmnl_update_chart(trmnl_update_object, config.trmnl_url_chart_1, config.trmnl_headers)
# Prepare a new TRMNL update
print("Constructing a TERMNL update object...", end=" ", flush=True)
trmnl_update_object = functions.prep_trmnl_chart_udpate(
history_per_ticker_filtered,
series_to_show_1="total",
data_to_show_1="current_irr",
series_to_show_2="DBPG.DE",
data_to_show_2="current_irr"
)
# Push the update to TRMNL
print("Updating a TERMNL screen...", end=" ", flush=True)
functions.push_trmnl_update_chart(trmnl_update_object, config.trmnl_url_chart_2, config.trmnl_headers)
# --------------------------- #
# PART 4: Cool off and repeat #
# --------------------------- #
# Logging
print("Completed cycle at: {}".format(functions.datetime.datetime.now()))
print("Waiting a few minutes before the next execution")
print("---------------------------------------------------------------------------")
# Clear variables
trades = {}
yf_data = {}
history_per_trade = {}
tickers = []
# Wait for api-cooldown
functions.time.sleep(config.programm_cooldown_time * 60)