Daten (Kurs-/Fundamental-) von ariva.de importieren

Hallo

Ein tolles Tool. Genau das, was ich gesucht habe. Vielen Dank für die Mühen!
Da Faulheit bekanntlich Innovation gebiert, habe ich mich mal hingesetzt und ein Ariva-Export-Tool in Python geschrieben, welches basierend auf WKN/ISIN einzeln oder aus nem PP-Export-CSV den notwendigen Link sowie ein Import-CSV für historische Daten erstellt. Toll wäre es, wenn es in PP einen Batchimport gäbe, aber der Import von Hand ist zumindest einfacher, wenn man nicht von Hand die Links und CSVs bei Ariva generieren muss. Hier der Code zur allgemeinen Verfügung (Edit: natürlich kurz nach dem Einstellen noch einen Fehler in einer Regex gefunden und korrigiert):

#! /usr/bin/python

# Obtain historical stock data from ariva to use with Portfolio Performance (or something else)
# V0.1 - 2021/01/10 Maik Goette : First running version

import os,sys,argparse,csv,time,re

from urllib import request, parse, error as urlerror
from datetime import date
from pathlib import Path

parser = argparse.ArgumentParser(description='This script builds links and downloads historical data for given stock identifiers',
								formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('id', metavar='ID', type=str,
                    help='Asset identifier (ISIN|WKN) or PP stock data export file. Symbol is NOT supported, yet')
parser.add_argument('--out', nargs='?', type=Path,
                     default=Path.cwd(),
					 help='Path for data files (csv). Default: '+str(Path.cwd()))					
parser.add_argument('--past', type=str,
                    default='1.1.2000',
                    help='Starting time for historical data CSV. Default: 1.1.2000')
parser.add_argument('--delimiter', type=str,
                    default=';',
                    help='CSV-Delimiter. Default: \";\"')
parser.add_argument('--currency', dest='cur', type=str,
                    default='EUR',
                    help='Currency EUR or USD. Default: EUR')
parser.add_argument('--exchange', dest='stex', type=str,
                    default='Xetra',
                    help='''Stock exchange to obtain data from (select one, Default: Xetra):
						Xetra
						Tradegate
						LS
						Gettex
						Nasdaq
						Nasdaq-OTC
					''')
parser.add_argument('--allexchanges', dest='all', action='store_true',
                    help='If this flag is given, all supported stock exchanges are searched. Results from exchange with longest history are used. USE WITH CARE!')
					
args = parser.parse_args()

## Translation table for ariva stock exchanges
stodic = {'xetra':'6',
		'tradegate':'131',
		'ls':'16',
		'gettex':'207',
		'nasdaq':'40',
		'nasdaq-otc':'83'}

def check_asset(stock, exch, curr):
	exchused = exch.upper()
	stocklist = []
	stockhist = '0' # Used as initial length counter
	stocklist.append(exch)
	if args.all:
		for key in stodic.keys():
			if key.lower() != exch.lower():
				stocklist.append(key)
	for item in stocklist:
		linko = "https://www.ariva.de/{0}/historische_kurse?go=1&boerse_id={1}&month=&currency={2}&clean_split=1&clean_bezug=1".format(stock, stodic[item.lower()], curr)
		try: # Have to cope with 403 forbidden sites 
			resu = request.urlopen(linko)
		except urlerror.HTTPError as e:
			print("ERROR: Historical data not available for {0} due to website error: {1}\nDetails: {2}".format(stock, e.code, e.__dict__))
			return -1
		except urlerror.URLError as e:
			print("ERROR: Historical data not available for {0} due to website error: {1}\nDetails: {2}".format(stock, e.code, e.__dict__))
			return -1
		if resu.code == 200:
			# read website content to parse
			rdata = resu.read().decode('utf-8')
			stockdata = str(re.findall(r'Keine aktuellen Kursdaten',rdata))
			if len(stockdata) > 2: # Have no clue why its 2 instead of zero, but it's so
				print("WARNING: No stockdata found on {0} for {1}.".format(item.upper(), stock))
				continue
			stockid = str(re.findall(r'name="secu"\ value.+?(?=\ \/>)',rdata)[0].split('=')[2].strip('\"'))
			# The next two lines are somewhat insane an likely VERY error prone
			subst_list = ['/', 'span', 'itemprop="productID"','<','>',' ','Typ']
			stockinfos = ' '.join(re.sub('|'.join(subst_list), '', str(re.findall(r'(?s)>WKN.*?>Typ',rdata)[0].replace('\n','').replace('\t',''))).split('div')).split()
			if len(stockinfos) < 3: # Fix if no stock symbol exists
				stockinfos.append('Symbol:')
			ahist = obtain_ariva_history(stockid, args.past, today(), stodic[item.lower()], args.cur).read().decode("utf-8")
			if len(ahist) > len(stockhist):
				stockhist = ahist
				exchused = item.upper()
				finallink = linko
		else:
			print("ERROR: Website access yields Code {0}. Manually check the link: {1}".format(resu.code, linko))
			return -1
	return finallink, stockhist, exchused, stockinfos

def obtain_ariva_history(stockid, minti, maxti, exch, curr):
	'''
	Function returns an object with CSV data
	'''
	csvurl = 'https://www.ariva.de/quote/historic/historic.csv'
	csvparam = {"clean_split":"1",  # Splits taken out
				"secu":stockid,
				"boerse_id":exch,
				"clean_bezug":"1",	# options taken out
				"clean_payout":"",	# Dividents NOT taken out
				"trenner":";",		# Delimiter for export
				"currency":curr,
				"min_time":minti,
				"max_time":maxti}
	qstring = parse.urlencode(csvparam)
	csvquery = csvurl + '?' + qstring
	csvresp = request.urlopen(csvquery)
	return csvresp

def today():
	format = '%d.%m.%Y'
	return date.today().strftime(format)
	
def write_csv(path, data, tag):
	fixedpath = path.joinpath(tag+'_history.csv')
	csvfile = open(fixedpath, "w")
	csvfile.write(data)
	csvfile.close()

def main():
	# here we test the asset string to check if its a file. not very elegant, but hey...
	# To ease up the Rest we build a 1-item dictionary for the no-file-based apporach, too
	reader = []
	if Path(args.id).is_file():
		print("\nINFO: Using filebased apporach with file {}".format(args.id))
		with open(Path(args.id), 'r', encoding='utf8', newline='') as csvfile:
			for line in csv.DictReader(csvfile, delimiter=args.delimiter):
				reader.append(line)
	else:
		if len(args.id) == 12: # This is ISIN
			reader.append({'ISIN':args.id,
							'WKN':''})
		elif len(args.id) == 6: # This is WKN
			reader.append({'WKN':args.id,
							'ISIN':''})
		else:
			print("ERROR: Something went wrong here. Found neither file nor a valid ISIN/WKN-length string was given")
			sys.exit(1)
	# Perform the magic!
	linklist = []
	print("\nExchange : WKN : ISIN : Symbol : Ariva-Link\n")
	for item in reader:
		picker = False
		if 'ISIN' in item and len(item['ISIN']) == 12:
			picker = item['ISIN']
		elif 'WKN' in item and len(item['ISIN']) == 6:
			picker = item['WKN']
		else: # If this is hit its likely a problem with the import file
			print("ERROR: Something went wrong here. Check your Input-CSV-file for entry: {}".format(item))
			continue
		try: # Have to try due to possible 403 Errors in function
			linko, stockhist, stockexch, stockids = check_asset(picker, args.stex, args.cur)
		except:
			continue
		if len(re.findall(r'\n',stockhist)) < 7 or not stockexch: # There are ALWAYS 5 LF at the end of each CSV + 1 for the header...therefor below 7 its empty.
			print("WARNING: No history data found for asset {}.".format(picker))
		else:
			write_csv(args.out, stockhist, picker)
		linklist.append(stockexch+','+stockids[0].split(':')[1]+','+stockids[1].split(':')[1]+','+stockids[2].split(':')[1]+','+linko)
		print("{0} : {1} : {2} : {3} : {4}".format(stockexch,stockids[0].split(':')[1],stockids[1].split(':')[1],stockids[2].split(':')[1], linko))
	linkfile = open(args.out.joinpath('linklist.csv'), 'w')
	linklist = map(lambda x:x+'\n', linklist)
	linkfile.writelines(linklist)
	linkfile.close()
	
if __name__ == "__main__":
    main()

1 Like

Hallo zusammen,
ich bin hier schon länger stiller Mitleser und begeisterter PP Nutzer.
Nun aber meine erste Frage die ich so noch nicht lösen konnte.
Meine historischen Kurse weichen stets vom Kurs bei Onvista am Buchungstag ab.
Ich nutze ariva als Datenquelle.
Bspw. für den MSCI World

https://www.ariva.de/IE00B4L5Y983/historische_kurse?boerse_id=12&month={DATE:yyyy-MM-32}&clean_split=1&clean_split=0&clean_payout=0&clean_bezug=1&clean_bezug=0

Bei einer Buchung bspw. am 15.06.2020 nennt Onvista einen Kurs von 50,692€ am Handelsplatz XETRA
Der in PP importierte historische Kurs ist jedoch 51,56€, was dem Schlusskurs des besagten Tages entspricht. Kann ich die URL so anpassen, dass stets der Startkurs des jeweiligen Tages gezogen wird?

Danke euch!

Nein.

Was ist denn das Problem? Das der Kaufkurs nicht dem Schlusskurs entspricht? Das ist doch die Normalität.

1 Like

Das Problem ist, dass ich jedes Mal alle Buchungen in PP manuell korrigieren muss, weil sonst mein gesamtes Depot nicht der Realität entspricht.
Der Kaufkurs ist (zumindest in meinem Fall bei Onvista) immer der erste Kurs des eingestellten Tages und eben nicht der Schlusskurs. Das führt stets bei jeder Buchung zu Abweichungen und macht den Nutzen der Sparplan-Funktion irgendwie zunichte oder deutlich weniger hilfreich.

Die Frage dazu wie man dem Abhilfe schaffen könnte steht ja schon oben in meiner ersten Nachricht.

Nein, genauso ist der Sparplan gedacht! Diese Funktion dient in PP nur dazu Buchungen automatisch zu generieren. Man sollte/muss sowieso jede Buchung korrigieren/prüfen.

Vielleicht sollte man die Funktion Mal umbenennen.

Mag ja alles sein, aber ich wiederhole mich mit meiner Frage, ob man die URL für die historischen Kurse nicht anpassen kann, so dass er sich statt des Schlusskurses den Startkurs zieht.
Der Startkurs steht ja faktisch auch zur Verfügung bei ariva.

Und welchen Sinn hat das?

Falls Du die Tatsache, dass ich mich wiederhole, meinst, dann liegt der Sinn darin, dass hier über alles Mögliche diskutiert wird, aber nicht über das eigentliche Anliegen. Daher sehe ich mich gezwungen, mich zu wiederholen.
Ich möchte wissen ob bzw. wie man die ariva URL anpassen kann, so dass PP sich den Startkurs, nicht den Schlusskurs, eines Tages zieht.
Nicht mehr und nicht weniger.

Ich denke nicht, dass das geht, da die Auswahl der Spalte kein Parameter der URL/Website ist, sondern in PP implementiert.

1 Like

Die Antwort hast du längst bekommen:

Verstehe.
Wäre dann vielleicht ein schönes Feature für eins der kommenden Updates.
Würde die Sparplanfunktion zumindest für Onvista Kunden vollkommen machen und jegliche manuelle Korrektur im Nachgang wäre obsolet.

Hallo zusammen,

übersehe ich da etwas? Denn der Kurs von Ariva ist doch eine Webseite, und beim Import kann man sich doch die Spalte aussuchen. Also auch den Schlusskurs.

Viele Grüße

Habak

Ich vermute mal, dass man neben dem Schlusskurs auch die anderen Indikatoren (Start, Hoch, Tief) speichern könnte um dann auswählen zu können, welcher für die Erstellung der Sparpläne genutzt wird. Würde halt den Speicher für historische Kurse vervierfachen.

Und dann die Frage, soll global eingestellt werden, welche Kurse für alles (wirklich für alles, auch Performance etc?) genutzt werden, oder global je Wertpapier die Einstellung vorgenommen werden (als Attribut?) oder in jedem Menüpunkt (für jedes Wertpapier?) einzeln?
Wenn man es hier jedem recht machen will wird das auf jeden Fall auf Kosten der Bedienerfreundlichkeit gehen.

Außerdem sehe ich nicht, dass der Vorschlag das gestellte Problem behebt. MEINE Sparpläne werden nicht zu Handelsbeginn ausgeführt (wofür ich sehr dankbar bin), ICH hätte also nichts von so einer Umstellung. Und das Onvista die Sparpläne exakt zum Startkurs (von XETRA) ausführt halte ich auch für fraglich.
Wie schon geschreben wurde, der Sparplan ist nur dafür da, dir die Buchungshülle zu generieren, die Details musst du selbst aus den Abrechnungen editieren. Das ist bekannt und wird glaube ich auch von jedem akzeptiert und angewendet. Alternativ kann man den pdf Import nutzen, dann braucht es die Sparplanfunktion nicht.

2 Likes

Die Sparpläne werden zumindest immer zwischen 9:02 und 9:06 Uhr ausgeführt. Und der Ausführungskurs entsprach in allen Stichproben dem XETRA Startkurs des entsprechenden Tages.

Ich würde keine so feingranulare Einstellmöglichkeit anbieten. Vorausgesetzt man könnte die URL dementsprechend anpassen, dass nur der gewünschte Kurs gezogen wird, wäre ja schon alles klar. Einmal in der Wertpapierübersicht bei historische Kurse die richtige URL hinterlegen und alles ist fein.

sehr coole Idee, genau die hatte ich auch ^^ Läuft auch durch, habe leider nur das Problem, dass der Link nicht die kompletten Daten liefert, die mir auch in die csv geschrieben werden.

Eingabe + Ausgabe:
(base) C:\Portofolio_Performance_skripts>python ariva_abruf.py --exchange Tradegate CA9838911027

Exchange : WKN : ISIN : Symbol : Ariva-Link

TRADEGATE : A0RPQ3 : CA9838911027 : XEBEF : https://www.ariva.de/CA9838911027/historische_kurse?go=1&boerse_id=131&month=&currency=EUR&clean_split=1&clean_bezug=1

in der csv sind die kompletten Daten seit Listing aber die CSV liefert nur bis Januar 2012

Hast du eine Idee woran es liegt?

Wenn ich dich richtig verstehe, erwartest du über den link die history daten. Das funktioniert so nicht. In PP hinterlegst du den link für die Daten der letzten 30 Tage zum aktualisieren. Die historischen Daten können nur über die Importfunktion per CSV eingelesen werden.