<gesmes:Envelope>
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender>
<gesmes:name>European Central Bank</gesmes:name>
</gesmes:Sender>
<Cube>
<Cube time="2011-09-09">
<Cube currency="USD" rate="1.3817"/>
<Cube currency="JPY" rate="107.48"/>
<Cube currency="BGN" rate="1.9558"/>
<Cube currency="CZK" rate="24.430"/>
<Cube currency="DKK" rate="7.4473"/>
<Cube currency="GBP" rate="0.86590"/>
</Cube>
</Cube>
</gesmes:Envelope>
There are three files available
HERE (and updated once daily) that contain:
Function GetECB_FX(ByVal XMLFile As Integer) As Boolean
If My.Computer.Network.IsAvailable Then
Dim xURI As String = Nothing
Select Case XMLFile
Case 0
xURI = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
Case 1
xURI = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml"
Case 2
xURI = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml"
End Select
Try
Using qClient As New Net.WebClient
qClient.Headers.Add("Accept-Encoding", "gzip")
qClient.DownloadDataAsync(New Uri(xURI))
AddHandler qClient.DownloadProgressChanged, AddressOf FXDownloadProgressCallback
AddHandler qClient.DownloadDataCompleted, AddressOf FXDownloadComplete1
End Using
Catch ex As Exception
Label3.Text = "Error downloading FX Rates. " & Err.Number & ": " & Err.Description
Return False
End Try
Else
Label3.Text = "Please connect to the internet to download FX Rates."
Return False
End If
Return True
End Function
Next are the handlers. For the progress changed, I have a label placed strategically on my form (Label3) to which I update myself of the progress of the download. Since I am using the
DownloadDataAsync method, it is non blocking and therefore will not freeze the UI while the download is taking place. Heres the code for the prgress handler.
Sub FXDownloadProgressCallback(ByVal sender As Object, ByVal e As Net.DownloadProgressChangedEventArgs)
xTimer.Interval = 10000
Dim xRec, xTot As Double
xRec = FormatNumber(e.BytesReceived / 1048576, 2)
xTot = FormatNumber(e.TotalBytesToReceive / 1048576, 2)
Label3.Text = "FX Rates download: " & e.ProgressPercentage & "% Completed. (" & xRec & "MB of " & xTot & "MB)"
End Sub
The next handler which is executed when the download of the file is complete, is the most important one. It carries out two tasks.
Using xReader As New XmlTextReader(New MemoryStream(e.Result))
I also utilise a couple of
dictionaries in parisng the XML. They are the
xDic which holds the individual rates as the file is parsed. The key is the currency and the value is the rate to the EURO, and the
FXDic whose key is the FX date and value is the
xDic above.
xDate = Date.ParseExact(xReader.Value, "yyyy-MM-dd", CultureInfo.InvariantCulture)
FXDic.Add(xDate, Nothing)
Since we are iterating through the nodes, we also need to check whether values for the FX rates have been populated (see
b below), and also to reset the Dictionary for those values, and then move to the time attribute that holds the date i.e
If xDic.Keys.Count > 0 Then FXDic.Item(xDate) = xDic
xDic = New SortedDictionary(Of String, Double)
xReader.MoveToAttribute("time")
b) As soon as we enter this statement, we move to the
currency attribute to get the
ISO 4217 currency and funds name and code elements and get its value, then move to the
rate attribute to get the FX rate, finally adding these to the
xDic, i.e
xReader.MoveToAttribute("currency")
Dim GCur = xReader.Value
xReader.MoveToAttribute("rate")
Dim GRate = xReader.Value
xDic.Add(GCur, GRate)
When reading the file is complete (at the end of the
While xReader.Read()) we need to add the last
xDic to the
FXDic because after reading the last two attribute Code node, we never encounter another one attribute code node, thus
If xDic.Keys.Count > 0 Then FXDic.Item(xDate) = xDic
Dim HDList As New List(Of String)
Array.ForEach(FXDic.Keys.ToArray, Sub(x) If Not FXDic.Item(x) Is Nothing Then HDList.AddRange(FXDic.Item(x).Keys.ToArray))
Next I use the list above to create and name DataColumns to add to the table, but first, Ihave toadd the column to hold the date and make it unique. Heres the code for that:
Dim FXTable As DataTable = New DataTable
With FXTable
.TableName = "ECB_FXTabe"
.Columns.Add(New DataColumn("Date") With {.DataType = GetType(System.DateTime), .ColumnName = "Date", .Unique = True})
Array.ForEach(HDList.Distinct.OrderBy(Function(x) x).ToArray, Sub(x) .Columns.Add(New DataColumn(x) With {.DataType = GetType(System.Double), .DefaultValue = 1.0, .ColumnName = x}))
End With
NOTE:I use distinct items (and sort them) from the list to create the table columns, i.e
HDList.Distinct.OrderBy(FuDim xRow As DataRow
For Each q In FXDic.Keys 'q is the value for date column
xRow = FXTable.NewRow()
xRow.Item("Date") = q
Array.ForEach(FXDic.Item(q).Keys.ToArray, Sub(x) xRow.Item(x) = CDbl(FXDic.Item(q).Item(x)))
FXTable.Rows.Add(xRow)
Next
Function GetFX(Optional ByVal xURI As String = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml") As Boolean
If My.Computer.Network.IsAvailable Then
Select Case My.Settings.FXLink
Case 0
xURI = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
Case 1
xURI = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml"
Case 2
xURI = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml"
End Select
Try
Using qClient As New Net.WebClient
qClient.Headers.Add("Accept-Encoding", "gzip")
qClient.DownloadDataAsync(New Uri(xURI))
AddHandler qClient.DownloadProgressChanged, AddressOf FXDownloadProgressCallback
AddHandler qClient.DownloadDataCompleted, AddressOf FXDownloadComplete1
End Using
Catch ex As Exception
Label3.Text = "Error downloading FX Rates. " & Err.Number & ": " & Err.Description
Return False
End Try
Else
Label3.Text = "Please connect to the internet to download FX Rates."
Return False
End If
Return True
End Function
Sub FXDownloadProgressCallback(ByVal sender As Object, ByVal e As Net.DownloadProgressChangedEventArgs)
xTimer.Interval = 10000
Dim xRec, xTot As Double
xRec = FormatNumber(e.BytesReceived / 1048576, 2)
xTot = FormatNumber(e.TotalBytesToReceive / 1048576, 2)
Label3.Text = "FX Rates download: " & e.ProgressPercentage & "% Completed. (" & xRec & "MB of " & xTot & "MB)"
End Sub
Sub FXDownloadComplete1(ByVal sender As Object, ByVal e As Net.DownloadDataCompletedEventArgs)
Dim FXDic As New Dictionary(Of Date, SortedDictionary(Of String, Double))
Dim xDic As New SortedDictionary(Of String, Double)
Dim xDate As Date = Nothing
Using xReader As New XmlTextReader(New MemoryStream(e.Result))
While xReader.Read()
If String.IsNullOrEmpty(xReader.Name) Then Continue While
If Not xReader.Name = "Cube" Then Continue While
Select Case xReader.AttributeCount
Case 1
If xDic.Keys.Count > 0 Then FXDic.Item(xDate) = xDic
xDic = New SortedDictionary(Of String, Double)
xReader.MoveToAttribute("time")
xDate = Date.ParseExact(xReader.Value, "yyyy-MM-dd", CultureInfo.InvariantCulture)
FXDic.Add(xDate, Nothing)
Case 2
xReader.MoveToAttribute("currency")
Dim GCur = xReader.Value
xReader.MoveToAttribute("rate")
Dim GRate = xReader.Value
xDic.Add(GCur, GRate)
End Select
xReader.MoveToNextAttribute()
End While
If xDic.Keys.Count > 0 Then FXDic.Item(xDate) = xDic
End Using
Dim HDList As New List(Of String)
Array.ForEach(FXDic.Keys.ToArray, Sub(x) If Not FXDic.Item(x) Is Nothing Then HDList.AddRange(FXDic.Item(x).Keys.ToArray))
Dim FXTable As New DataTable
With FXTable
.TableName = "ECB_FXTabe"
.Columns.Add(New DataColumn("Date") With {.DataType = GetType(System.DateTime), .ColumnName = "Date", .Unique = True})
Array.ForEach(HDList.Distinct.OrderBy(Function(x) x).ToArray, Sub(x) .Columns.Add(New DataColumn(x) With {.DataType = GetType(System.Double), .DefaultValue = 1.0, .ColumnName = x}))
End With
Dim xRow As DataRow
For Each q In FXDic.Keys 'q is the value for date column
xRow = FXTable.NewRow()
xRow.Item("Date") = q
Array.ForEach(FXDic.Item(q).Keys.ToArray, Sub(x) xRow.Item(x) = CDbl(FXDic.Item(q).Item(x)))
FXTable.Rows.Add(xRow)
Next
End Sub
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)