xml linq Abfrage bei Währungskursen

  • VB.NET

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von ATKRotschopf.

    xml linq Abfrage bei Währungskursen

    Hier im Forum gab es ja schon das Beispiel wie man die Währungskurse bei der EZB abfragen kann.
    Das habe ich genommen und muss das nun erweitern, dass ich in der Liste die Kurse der letzten 90 Tage habe.

    Hierzu gibt es folgende Klasse:

    VB.NET-Quellcode

    1. Imports System.Globalization
    2. ' ECB Struktur
    3. Public Structure ECBType
    4. Public ECBCurrency As String
    5. Public ECBRate As String
    6. Public DisplayName As String
    7. Public ECBDate As Date
    8. End Structure
    9. Public Class ECBExchanges
    10. Public Function getECBCurrencyExchanges90(ByVal WebAddress As String) _
    11. As List(Of ECBType)
    12. Try
    13. Dim xr As XElement = XElement.Load(WebAddress)
    14. Dim xn As XNamespace = xr.Attribute("xmlns").Value
    15. Dim xECBs = From k1 In xr.Descendants(xn + "Cube")
    16. From k2 In k1.Elements()
    17. Select New ECBType With {
    18. .ECBCurrency = k2.Attribute("currency").Value,
    19. .ECBRate = k2.Attribute("rate").Value,
    20. .DisplayName = CurrencyName(.ECBCurrency),
    21. .ECBDate = DateTime.Parse(k1.Attribute("time").Value)}
    22. Return xECBs.ToList
    23. Catch ex As Exception
    24. Throw ex
    25. End Try
    26. End Function
    27. Private Function CurrencyName(ByVal isoCode As String) As String
    28. Dim cultures As CultureInfo() = CultureInfo.GetCultures( _
    29. CultureTypes.SpecificCultures)
    30. For Each ci As CultureInfo In cultures
    31. Dim ri As New RegionInfo(ci.LCID)
    32. If ri.ISOCurrencySymbol = isoCode Then
    33. Return ci.DisplayName
    34. End If
    35. Next
    36. Return String.Empty
    37. End Function
    38. End Class


    Der Aufruf mit der WebAdresse findet dann so statt:

    VB.NET-Quellcode

    1. Dim ECBList90 As List(Of ECBType) = MyECB.getECBCurrencyExchanges90( _
    2. "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml")


    Die xml der EZB sieht wie folgt aus:
    Spoiler anzeigen

    XML-Quellcode

    1. <gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
    2. <gesmes:subject>Reference rates</gesmes:subject>
    3. <gesmes:Sender>
    4. <gesmes:name>European Central Bank</gesmes:name>
    5. </gesmes:Sender>
    6. <Cube>
    7. <Cube time="2013-07-17">
    8. <Cube currency="USD" rate="1.3136"/>
    9. <Cube currency="JPY" rate="131.15"/>
    10. <Cube currency="BGN" rate="1.9558"/>
    11. <Cube currency="CZK" rate="25.968"/>
    12. <Cube currency="DKK" rate="7.4573"/>
    13. <Cube currency="GBP" rate="0.86315"/>
    14. <Cube currency="HUF" rate="293.85"/>
    15. <Cube currency="LTL" rate="3.4528"/>
    16. <Cube currency="LVL" rate="0.7025"/>
    17. <Cube currency="PLN" rate="4.261"/>
    18. <Cube currency="RON" rate="4.4457"/>
    19. <Cube currency="SEK" rate="8.6295"/>
    20. <Cube currency="CHF" rate="1.234"/>
    21. <Cube currency="NOK" rate="7.8775"/>
    22. <Cube currency="HRK" rate="7.516"/>
    23. <Cube currency="RUB" rate="42.628"/>
    24. <Cube currency="TRY" rate="2.5299"/>
    25. <Cube currency="AUD" rate="1.4273"/>
    26. <Cube currency="BRL" rate="2.9669"/>
    27. <Cube currency="CAD" rate="1.3656"/>
    28. <Cube currency="CNY" rate="8.0595"/>
    29. <Cube currency="HKD" rate="10.1903"/>
    30. <Cube currency="IDR" rate="13192.75"/>
    31. <Cube currency="ILS" rate="4.6913"/>
    32. <Cube currency="INR" rate="78.113"/>
    33. <Cube currency="KRW" rate="1469.11"/>
    34. <Cube currency="MXN" rate="16.6105"/>
    35. <Cube currency="MYR" rate="4.1924"/>
    36. <Cube currency="NZD" rate="1.6725"/>
    37. <Cube currency="PHP" rate="56.954"/>
    38. <Cube currency="SGD" rate="1.6599"/>
    39. <Cube currency="THB" rate="40.84"/>
    40. <Cube currency="ZAR" rate="13.0168"/>
    41. </Cube>
    42. <Cube time="2013-07-16">
    43. <Cube currency="USD" rate="1.3118"/>
    44. <Cube currency="JPY" rate="130.35"/>
    45. <Cube currency="BGN" rate="1.9558"/>
    46. <Cube currency="CZK" rate="25.96"/>
    47. <Cube currency="DKK" rate="7.4571"/>
    48. <Cube currency="GBP" rate="0.8693"/>
    49. <Cube currency="HUF" rate="292.6"/>
    50. <Cube currency="LTL" rate="3.4528"/>
    51. <Cube currency="LVL" rate="0.7025"/>
    52. <Cube currency="PLN" rate="4.256"/>
    53. <Cube currency="RON" rate="4.436"/>
    54. <Cube currency="SEK" rate="8.6974"/>
    55. <Cube currency="CHF" rate="1.2371"/>
    56. <Cube currency="NOK" rate="7.8935"/>
    57. <Cube currency="HRK" rate="7.5235"/>
    58. <Cube currency="RUB" rate="42.5644"/>
    59. <Cube currency="TRY" rate="2.5231"/>
    60. <Cube currency="AUD" rate="1.4225"/>
    61. <Cube currency="BRL" rate="2.9022"/>
    62. <Cube currency="CAD" rate="1.3657"/>
    63. <Cube currency="CNY" rate="8.0479"/>
    64. <Cube currency="HKD" rate="10.1767"/>
    65. <Cube currency="IDR" rate="13278.42"/>
    66. <Cube currency="ILS" rate="4.6784"/>
    67. <Cube currency="INR" rate="77.586"/>
    68. <Cube currency="KRW" rate="1466.21"/>
    69. <Cube currency="MXN" rate="16.5372"/>
    70. <Cube currency="MYR" rate="4.1672"/>
    71. <Cube currency="NZD" rate="1.667"/>
    72. <Cube currency="PHP" rate="56.69"/>
    73. <Cube currency="SGD" rate="1.6517"/>
    74. <Cube currency="THB" rate="40.731"/>
    75. <Cube currency="ZAR" rate="12.8506"/>
    76. </Cube>
    77. <Cube time="2013-07-15">
    78. <Cube currency="USD" rate="1.3012"/>
    79. <Cube currency="JPY" rate="130.68"/>
    80. <Cube currency="BGN" rate="1.9558"/>
    81. <Cube currency="CZK" rate="26.01"/>
    82. <Cube currency="DKK" rate="7.4576"/>
    83. <Cube currency="GBP" rate="0.8647"/>
    84. <Cube currency="HUF" rate="292.01"/>
    85. <Cube currency="LTL" rate="3.4528"/>
    86. <Cube currency="LVL" rate="0.7026"/>
    87. <Cube currency="PLN" rate="4.2855"/>
    88. <Cube currency="RON" rate="4.4236"/>
    89. <Cube currency="SEK" rate="8.7359"/>
    90. <Cube currency="CHF" rate="1.2394"/>
    91. <Cube currency="NOK" rate="7.931"/>
    92. <Cube currency="HRK" rate="7.5315"/>
    93. <Cube currency="RUB" rate="42.544"/>
    94. <Cube currency="TRY" rate="2.5303"/>
    95. <Cube currency="AUD" rate="1.438"/>
    96. <Cube currency="BRL" rate="2.9516"/>
    97. <Cube currency="CAD" rate="1.3568"/>
    98. <Cube currency="CNY" rate="7.9865"/>
    99. <Cube currency="HKD" rate="10.095"/>
    100. <Cube currency="IDR" rate="13034.32"/>
    101. <Cube currency="ILS" rate="4.6835"/>
    102. <Cube currency="INR" rate="77.935"/>
    103. <Cube currency="KRW" rate="1461.48"/>
    104. <Cube currency="MXN" rate="16.6489"/>
    105. <Cube currency="MYR" rate="4.1513"/>
    106. <Cube currency="NZD" rate="1.6803"/>
    107. <Cube currency="PHP" rate="56.495"/>
    108. <Cube currency="SGD" rate="1.6491"/>
    109. <Cube currency="THB" rate="40.571"/>
    110. <Cube currency="ZAR" rate="12.9362"/>
    111. </Cube>
    112. <Cube time="2013-07-12">
    113. <Cube currency="USD" rate="1.3034"/>
    114. <Cube currency="JPY" rate="129.75"/>
    115. <Cube currency="BGN" rate="1.9558"/>
    116. <Cube currency="CZK" rate="25.951"/>
    117. <Cube currency="DKK" rate="7.4581"/>
    118. <Cube currency="GBP" rate="0.8627"/>
    119. <Cube currency="HUF" rate="292.82"/>
    120. <Cube currency="LTL" rate="3.4528"/>
    121. <Cube currency="LVL" rate="0.7027"/>
    122. <Cube currency="PLN" rate="4.3193"/>
    123. <Cube currency="RON" rate="4.4227"/>
    124. <Cube currency="SEK" rate="8.705"/>
    125. <Cube currency="CHF" rate="1.2388"/>
    126. <Cube currency="NOK" rate="7.913"/>
    127. <Cube currency="HRK" rate="7.532"/>
    128. <Cube currency="RUB" rate="42.685"/>
    129. <Cube currency="TRY" rate="2.5599"/>
    130. <Cube currency="AUD" rate="1.4436"/>
    131. <Cube currency="BRL" rate="2.9542"/>
    132. <Cube currency="CAD" rate="1.3537"/>
    133. <Cube currency="CNY" rate="8.0005"/>
    134. <Cube currency="HKD" rate="10.1106"/>
    135. <Cube currency="IDR" rate="13021.77"/>
    136. <Cube currency="ILS" rate="4.6921"/>
    137. <Cube currency="INR" rate="77.722"/>
    138. <Cube currency="KRW" rate="1467.36"/>
    139. <Cube currency="MXN" rate="16.7474"/>
    140. <Cube currency="MYR" rate="4.1416"/>
    141. <Cube currency="NZD" rate="1.6752"/>
    142. <Cube currency="PHP" rate="56.563"/>
    143. <Cube currency="SGD" rate="1.65"/>
    144. <Cube currency="THB" rate="40.64"/>
    145. <Cube currency="ZAR" rate="13.093"/>
    146. </Cube>
    147. </Cube>
    148. </gesmes:Envelope>



    Leider schlägt das ganze mit einem Objektverweis fehl und weiss nicht warum.
    Ja ich denke das wird ein DataTable werden, zwar mit 4 Spalten
    ISO-Land|Bezeichnung|Rate|Datum
    Aber da sollte das dann alles rein passen.

    Generell die Frage, funktionieren die Abfragegeschichten so wie ich sie da haben?
    Wenn du nur einzelne Währungen abrufen willst, kannst du sie ja einfach in eine Liste laden:

    VB.NET-Quellcode

    1. Private Function getDevisen(ByVal Currency As String) As List(Of Decimal)
    2. Dim eXML As XElement = XElement.Load("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml")
    3. Dim nspXML As XNamespace = eXML.Attribute("xmlns").Value
    4. Return (From EZB In eXML.Descendants(nspXML + "Cube")
    5. Where EZB.Attribute("currency") = Currency AndAlso EZB.Attribute("rate") IsNot Nothing
    6. Select Convert.ToDecimal(EZB.Attribute("rate").Value)).ToList
    7. End Function

    und dann mit den Daten div. Berechnungen durchführen...

    ATKRotschopf schrieb:

    Ja ich denke das wird ein DataTable werden, zwar mit 4 Spalten
    ISO-Land|Bezeichnung|Rate|Datum
    Aber da sollte das dann alles rein passen.
    Als Datenbänker stimmt mich das traurig, denn dabei kommt heraus, dass jedes Datum mehrmals vorkommt in der Tabelle, und jede Redundanz rächt sich über kurz oder lang.

    Generell die Frage, funktionieren die Abfragegeschichten so wie ich sie da haben?
    woher soll ich das wissen?
    Solange ichs nicht ausprobiert hab, gibts nur einen, der dazu eine Aussage machen kann: Du.
    Ich hatte heute noch mal ein Gespräch mit dem Kunden und da kommt es noch besser/schlimmer aber auf alle Fälle anders. Er möchte in einer eigenen Datenbank die Kurse alle historisch aufbewahren und vorhalten.
    Dazu gibt es einmal einen Lauf, der die Tageskurse abholt (gibt es gesondert von der EZB) und in eine Datenbank schreibt und dann noch mal einen Monatlichen Lauf, der prüft ob wirklich für jeden Tag die Währungen vorhanden sind (hier sind die 90 Tage wieder gefragt).

    Nun habe ich halt 2 Optionen wie ich die Tabelle aufbaue, entweder man hat als Spalten Datum|Pro Währung eine Spalte| und dann hat man das Datum nur einmal drin und nur einen Datensatz. Ungünstig wirds hier natürlich wenn eine neue Währung kommt.
    Oder man macht die Spalten Datum|Währung|Kurs, da habe ich dann natürlich die Schlüssel auf Datum und Währung.

    ErfinderDesRades schrieb:

    Als Datenbänker stimmt mich das traurig, denn dabei kommt heraus, dass jedes Datum mehrmals vorkommt in der Tabelle, und jede Redundanz rächt sich über kurz oder lang.
    In dem Fall könnte man einen Unique Key auf alle vier Felder legen, dann gibt es keine redundanten Datensätze.
    Falls jedoch ein Wert am Monatsende anders gemeldet wird als zum Tagesabschluss (kann schon mal vorkommen, dass da nachträglich Korrekturen erfolgen), dann ist es schon wichtig, beide Datensätze aufzubewahren, um eine historische Analyse nachvollziehbar zu machen.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    also in meinem armen Datenbänker-Hirn gibts genau 2 redundanzfreie Lösungen:
    1. eine Tabelle mit für jede Währung eine Spalte.
      Dassis einfach, aber starr - also wenn Währungen weg- oder zu-kommen muß neu gecodet werden
    2. 2 verknüpfte Tabellen
      für einen Datenbänker eiglich noch einfacher, sone Relation ist ja sein täglich Brot
    Warum sollteman zur redundanzhaltigen Lösung greifen, die doch gegen die 3.NormalForm verstößt - auch wenn da ein mehrspaltiger Primkey drauf liegt?
    Jetzt verstehe ich, was du meinst.
    Dich stört nicht die Redundanz im Datum, sondern die Redundanz an sich.
    Das kann ich wiederum nachvollziehen.

    ErfinderDesRades schrieb:

    eine Tabelle mit für jede Währung eine Spalte.
    So ähnlich arbeiten die professionellen Marktdatensysteme, die nichts anderes machen, als permanent tausende solcher Daten einzulagern.
    Da wird für jeden Wertetyp eine Zeitreihendefinition angelegt (z.B. "Tages-Schlusskurs EZB EUR->USD" und entsprechenden Beschreibungs- und Lookup-Parametern), deren Key wird dann verlinkt in der Datentabelle.
    Dassis einfach, aber starr - also wenn Währungen weg- oder zu-kommen muß neu gecodet werden
    Könnte man notfalls auch dynamisch machen.
    Wenn man keinen passenden Lookup findet, wird einfach eine neue Zeitreihendefinition angelegt.
    In der Praxis wird das aber eher selten gemacht.
    Da ist normalerweise die Forderung vom Business, welche Zeitreihen benötigt werden und das System hat sich dann darum zu kümmern, dass die Daten irgendwie gefüllt werden.
    Bei so Allerweltsdaten wie FX-Rates könnte man allerdings auch "Vorratsdatenspeicherung" betreiben und automatisch alle neuen Kurse mit einlagern.

    2 verknüpfte Tabellen
    Du meinst einerseits die Zeitreihendefinition und andererseits den Tag?
    Das wäre aber etwas scharf geschossen.
    Ein Datumsstempel ist (je nach Format) nicht länger als ein Index und nur der Redundanz zuliebe eine Tagestabelle anzulegen, halte ich für Onanie.
    Abgesehen davon beziehen sich viele Marktdaten nicht nur auf einen Tag, sondern auf (mindestens) einen Zeitstempel.

    Oder habe ich dich wieder missverstanden?
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „petaod“ ()

    Also ich denke mir dabei garnix besonders tiefschürfendes. Ich sehe einfach, zu einem Datum sind viele Währungs-Daten gegeben, also liegt eine 1:n - Relation vor, und so wird das Datenmodell dann auch modelliert.
    Also ich denke da einfach nach Schema F, und dassis halt das Standard-Vorgehen, um Redundanz zu entfernen, und um die 3. Normalform zu erreichen, und also ein Datenmodell nach Stand der Technik.
    Ich sehe hier keinen Bedarf für iwelche spezialisierten Optimierungen, und also keinen Grund, von Schema F abzuweichen.
    Grade Anfängern (nein, jedem, und bei jedem Problem) empfehle ich dringend, wann immer möglich, Schema F anzuwenden und einzuüben, das gilt ganz allgemein, aber inne Datenbänkerei erst recht.
    Spezialfälle und Komplikationen, wo Schema F nicht mehr akkurat ist, kommen ganz von selbst - dassis was, wo man sich sich drauf verlassen kann ;)
    Das heisst ja wenn ich in meinem Hinterkopf noch mal krame, ich müsste quasi 3 Tabellen erstellen
    1. Zeittabelle mit den Spalten Zeit_ID | Datum
    2. Währungstabelle mit den Spalten WKZ_ID | ISO-Kürzel | Bezeichnung
    3. Kurstabell mit den Spalten Zeit_ID | WKZ_ID | Kurs

    Damit sollte doch dann die DB Struktur so sein dass man damit anständig arbeiten kann oder trügt mich meine Erinnerung?
    Jo, das scheint mir, was hier Schema F ist.

    Daraus kannman zB. zweierlei View basteln: Kurse nach Datum, welcher pro Datum alle Währungen listet
    oder
    Kurse nach Währung, welcher pro Währung alle Kurse dieser Währung (chronologisch) listet.
    gugge das Sample in die relationale GrundIdee

    Dem EntityFramework-Designer habich eine ich finde leicht verständliche Notation der Relatinen von som Datenmodell abgeguckt:

    Waehrung->Kurs
    Datum->Kurs

    --- oder einzeilig: ----
    Waehrung->Kurs<-Datum

    Kannst dir auch mein Benamungs-Schema für DB-Entitäten, das ist gewissermaßen die Ausarbeitung von noch mehr Schema F, wo ich auch Wert lege auf kurze Namen ohne Sonderzeichen, in Singular und nach transparentem Schema festgelegt.
    Ausserdem wie man Schlüsselspalten vergibt und die Relationen einstellt.
    also nach meim Benamungs-Schema hießen die Tabellen und Spalten

    Quellcode

    1. Datum: Datum | ID
    2. Waehrung: Wkz | ISO | Name | ID
    3. Kurs: ID | DatumID | WaehrungID | Kurs
    dassis glaub das kürzeste bei gleichzeitig ganz eindeutig.

    Viele Beispiele findeste auch hier: Daten laden, speichern, verarbeiten

    Und dort schlage ich auch vor, Datenbanken ohne Datenbank zu entwickeln, und selbst am Ende noch zu überdenken, ob eine DB überhaupt nötig ist, oder ob nicht einfach reicht, das typisiertes Dataset direkt auf Platte zu schreiben.
    Ich schätze mal grob, 10 Jahre Währungsdaten würde in eine 2 - 4 MB Dataset-Datei passen.

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    zu einem Datum sind viele Währungs-Daten gegeben, also liegt eine 1:n - Relation vor
    Im hier vorgegebenen Beispiel hast du recht.
    Der Euro-Referenzkurs wird täglich um 13:15 Uhr (UTC) fixiert und gilt als das Standardmaß für Berechnungen, bei denen tagesgenaue Kurse ausreichend sind.
    Das Datum ist bei dieser Anwendung also nur der Sonderfall eines Timestamps, bei dem die Uhrzeit vernachlässigt wird.

    Ein Devisenhändler beispielsweise benötigt eine wesentlich genauere Granularität und wird die Kurse mindestens minutengenau speichern.
    Damit wird eine Redundanz eher zum Zufall als zu einem System, aus dem sich eine Design-Regel ableiten ließe.
    (Vom Algo-Trading im Millisekundentakt will ich jetzt gar nicht reden. Da gelten eh nochmals andere Regeln.)

    Aber der eigentliche Grund, weshalb man Timestamps nicht in eigene Tabellen auslagert, dürfte in der Weiterverarbeitung liegen.
    Schon bei einer einfachen Aggregation (z.B. Monatsdurchschnitt) würde der entsprechende Select unverhältnismäßig aufwendig (und lastintensiv) im Vergleich zur direkten Speicherung des Zeitstempels.

    Zeit ist ein Grunddatentyp einer Datenbank, der dazu noch per Definition sein Sortierkriterium impliziert hat.
    Ein Subset davon in eine eigene Tabelle auszulagern (und zur optimalen Selektion mit einem eigenen Index zu versehen) ist einfach kontraproduktiv.

    Die Steigerung davon wäre, eine Gruppe von verwendeten Ganzzahlen in eine Tabelle auszulagern und einen Index darauf zu legen, damit ich sortiert darauf zugreifen kann.
    Da nehme ich doch lieber ein Integer ;)
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Ich sehe schon auch, dasses grenzwertig ist, Redundanz auszulagern, die nur in einer einzigen Spalte besteht. Weil entsteht ja der Overhead einer Foreignkey-Spalte, also ob das letztlich Speicherplatz spart (Integer versus DateTime) ist auch sehr fraglich.
    Wie gesagt: Ich hange nur Schema F an, und grad zur WeiterVerarbeitung sehe ich ja den Vorteil, dass man dort weiter nach Schema F vorgehen kann:

    ErfinderDesRades schrieb:

    Daraus kannman zB. zweierlei View basteln: Kurse nach Datum, welcher pro Datum alle Währungen listet
    oder
    Kurse nach Währung, welcher pro Währung alle Kurse dieser Währung (chronologisch) listet.
    also es gibt nichts einfacheres als das - will man diese Views.

    Die Betrachtungen von Millisekunden-Brokern und Zeugs führen vom Thema weg, weil wir haben ja eine ganz klare Datenstruktur vorgegeben, und die enthält eine 1:n - Relation, und da wende ich mein Schema an.

    Weiterverarbeitung zb MonatsAggregation - a) hat der TE (noch?) nicht nachgefragt, und b) täte ich mit Linq2Dataset darauf losgehen - wie gesagt: Bei diesem Daten ists kein Problem, auch einen 10 Jahres-Bestsand im Speicher zu halten, und im Speicher ist Linq an Einfachheit, Geschwindigkeit und Resourcen-Schonung einem richtigen DB-Abruf hochhaus-hoch überlegen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()