Page 1 of 2 12 LastLast
Results 1 to 40 of 49

Thread: [RESOLVED] Help communicating with web API (bitfinex)

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Resolved [RESOLVED] Help communicating with web API (bitfinex)

    Hello guys, I am trying to communicate with a web API I found a VB.NET example but it encodes to Base64 and UTF8 and communicates via JSON (POST/GET)

    here is the link post #5

    I'm not sure how I could communicate with the API from vb6.
    I know I will need to encode to Base64 and then make a request fill the header and send the request using POST then wait for the reply (in a loop?) using GET?

    This is how to do it in VB.Net..

    Code:
    Sub GetBalance()
    
            Dim nonce As Long = CLng(DateTime.UtcNow.Subtract(New DateTime(1970, 1, 1)).TotalSeconds)
            'returns a strictly increasing timestamp based number e.g. 1402207693893
            Dim path As String = "https://api.bitfinex.com/v1/balances"
            Dim paramDict As String = "{""request"": ""/v1/balances"",""nonce"": """ & nonce & """}"
            'ie. {"request": "/v1/balances","nonce": "1402207693893"}
            Dim payload As String = Convert.ToBase64String(Encoding.UTF8.GetBytes(paramDict))
    
            'API Sign
            Dim hmac As New HMACSHA384(Encoding.UTF8.GetBytes("SECRET KEY GOES HERE"))
            'My API SECRET
            Dim hash As Byte() = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload))
            Dim hexHash As String = BitConverter.ToString(hash).Replace("-", "").ToLower()
    
            Dim headers As New NameValueCollection()
            headers.Add("X-BFX-APIKEY", "KEY GOES HERE")
            'My API KEY
            headers.Add("X-BFX-PAYLOAD", payload)
            headers.Add("X-BFX-SIGNATURE", hexHash)
    
            'POST data
            Try
                'create post request
                Dim request As HttpWebRequest = DirectCast(HttpWebRequest.Create(path), HttpWebRequest)
                request.KeepAlive = True
                request.Method = "POST"
    
                'add headers
                request.Headers.Add(headers)
    
                'write out payload
                Dim byteArray As Byte() = System.Text.Encoding.UTF8.GetBytes(paramDict)
                request.ContentLength = byteArray.Length
                Using writer = request.GetRequestStream()
                    writer.Write(byteArray, 0, byteArray.Length)
                End Using
    
                'read reply
                Using response = TryCast(request.GetResponse(), System.Net.HttpWebResponse)
                    Using reader = New System.IO.StreamReader(response.GetResponseStream())
                        'get reply (JSON)
                        Dim responseContent As String = reader.ReadToEnd()
                        MsgBox(responseContent)
                    End Using
                End Using
            Catch e As Exception
                'always throws an exception here
                Debug.WriteLine(e.Message)
            End Try
        End Sub


    Edit:
    I need help mostly with this part

    Code:
            Dim payload As String = Convert.ToBase64String(Encoding.UTF8.GetBytes(paramDict))
    
            'API Sign
            Dim hmac As New HMACSHA384(Encoding.UTF8.GetBytes("SECRET KEY GOES HERE"))
            'My API SECRET
            Dim hash As Byte() = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload))
            Dim hexHash As String = BitConverter.ToString(hash).Replace("-", "").ToLower()

    (Python Example if it helps)

    Another quick question is if it matters if I use inet or winsock or xmlhttp to send the header? As long as the information inside is correct it should work right?
    Last edited by Max187Boucher; Apr 18th, 2015 at 10:37 PM.

  2. #2

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: Help communicating with web API

    This is what I got so far looking for base64 function and not 100% sure what this 'HMACSHA384(Encoding.UTF8.GetBytes)' function is? does it simply encode to sha384? not sure what it is doing

    Code:
    Private Sub Command2_Click()
    Dim nonce As Long
    Dim path As String
    Dim sRequest As String
    Dim paramDict As String:
    Dim payload As String:
    
    'Dim hmac As New HMACSHA384(Encoding.UTF8.GetBytes("SECRET KEY GOES HERE")) '***** Not sure *******
    Dim hash As Byte
    Dim hexHash As String
    Dim header As New Collection
    
    
    
    nonce = CLng(Time) 'returns a strictly increasing timestamp based number e.g. 1402207693893
    path = "https://api.bitfinex.com/v1/balances"
    
    sRequest = "/v1/balances"
    
    paramDict = "{" & """request""" & ": " & sRequest & "," & """nonce""" & ": " & """ & nonce & """ & "}"
    payload = EncodeToBase64(paramDict) '******* Need Base64 Function ********
    
    hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload))  '**** Not Sure *****
    hash = BitConverter.ToString(hash).Replace("-", "").ToLower() 'convert bytes to string replace any "-" and LCase() the string then back to byte?
    
    header.Add """X-BFX-APIKEY""" & ": " & APIKEY
    header.Add """X-BFX-PAYLOAD""" & ": " & payload
    header.Add """X-BFX-SIGNATURE""" & ": " & hexHash
    
    Inet1.OpenURL path, header
    End Sub

  3. #3
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: Help communicating with web API

    Here's a VB6-class-encapsulation which covers the above,
    (it has dependencies to vbRichClient5 and WinHttp 5.1):

    Edit: Note, that the RichClient-version has to be at least 5.0.26 (the SHA384 and SHA512 methods were new additions to cCrypt)

    Code:
    Option Explicit
    
    Const BitFinexApiVersion$ = "/v1", BitFinexApiURL = "https://api.bitfinex.com" & BitFinexApiVersion
    
    Private mBitFinexKey As String, mBitFinexSecret As String, mCrypt As cCrypt
    
    Friend Sub Init(BitFinexKey As String, BitFinexSecret As String)
      mBitFinexKey = BitFinexKey
      mBitFinexSecret = BitFinexSecret
      Set mCrypt = New_c.Crypt
    End Sub
     
    Public Function DoAuthenticatedRPC(RequestSuffix As String, Result As cCollection, Optional httpStatusText As String, Optional ByVal PayLoad As cCollection) As Boolean
    Dim sPayload_UTF8_Base64 As String, http As New WinHttpRequest
    
     If PayLoad Is Nothing Then Set PayLoad = New_c.JSONObject
        PayLoad.Prop("request") = BitFinexApiVersion & RequestSuffix
        PayLoad.Prop("nonce") = CStr(New_c.Connection.UniqueID64) 'that's a time-based, increasing int64-value
     
        sPayload_UTF8_Base64 = mCrypt.Base64Enc(PayLoad.SerializeToJSONUTF8)
    
        http.Open "POST", BitFinexApiURL & RequestSuffix, False
        http.SetRequestHeader "X-BFX-APIKEY", mBitFinexKey
        http.SetRequestHeader "X-BFX-PAYLOAD", sPayload_UTF8_Base64
        http.SetRequestHeader "X-BFX-SIGNATURE", mCrypt.HMAC_SHA384(sPayload_UTF8_Base64, mCrypt.VBStringToUTF8(mBitFinexSecret))
        http.Send
        
        httpStatusText = http.Status & ": " & http.StatusText
        If http.Status <> 200 Then Exit Function
        Set Result = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
        DoAuthenticatedRPC = True
    End Function
     
    Public Function DoUnauthenticatedRPC(RequestSuffix As String, Result As cCollection, Optional httpStatusText As String, Optional ByVal PayLoad As cCollection) As Boolean
    Dim http As New WinHttpRequest, i As Long, ArgList As String
        If Not PayLoad Is Nothing Then
          For i = 0 To PayLoad.Count - 1
            ArgList = ArgList & IIf(i, "&", "?") & PayLoad.KeyByIndex(i) & "=" & PayLoad.ItemByIndex(i)
          Next i
        End If
        http.Open "GET", BitFinexApiURL & RequestSuffix & ArgList, False
        http.Send
        
        httpStatusText = http.Status & ": " & http.StatusText
        If http.Status <> 200 Then Exit Function
        Set Result = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
        DoUnauthenticatedRPC = True
    End Function

    Here's how to use the Class (I named it: cBitFinex)

    Code:
    Dim BitFinex As New cBitFinex
        BitFinex.Init "KEY HERE", "SECRET KEY HERE"
    
    Dim Result As cCollection, httpStatusText As String
    
    'Unauthenticated Request
    If BitFinex.DoUnauthenticatedRPC("/lendbook/btc", Result, httpStatusText) Then
      Debug.Print Result.SerializeToJSONString
    Else
      Debug.Print httpStatusText
    End If
    
    'Authenticated Request
    If BitFinex.DoAuthenticatedRPC("/balances", Result, httpStatusText) Then
      Debug.Print Result.SerializeToJSONString
    Else
      Debug.Print httpStatusText
    End If
    Couldn't really test it, not having an account at the site in question.

    Let me know, how it works on your end.

    Olaf
    Last edited by Schmidt; Apr 19th, 2015 at 02:28 AM. Reason: corrected the result-decoding to JSONDecodeToCollectionUTF8

  4. #4

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: Help communicating with web API

    Wow Olaf thanks a million will go and test will post back results!

  5. #5

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: Help communicating with web API

    Hey Olaf ( I am trying to figure it out but no luck so far) I get an Error object variable or with block variable not set
    This is the line
    Code:
    DoAuthenticatedRPC.Prop("httpStatus") = http.Status & ": " & http.StatusText

  6. #6

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: Help communicating with web API

    ok it is working (it did have my balance in the debug window) Olaf but I had to modify, if you could tell me why your code is making an error it would be fantastic, but here is the "working" version

    Code:
    Public Function DoAuthenticatedRPC(RequestSuffix As String, Optional ByVal PayLoad As cCollection) As cCollection
    Dim sPayload_UTF8_Base64 As String, http As New WinHttpRequest
    
     If PayLoad Is Nothing Then Set PayLoad = New_c.JSONObject
        PayLoad.Prop("request") = BitFinexApiVersion & RequestSuffix
        PayLoad.Prop("nonce") = CStr(New_c.Connection.UniqueID64) 'that's a time-based, increasing int64-value
     
        sPayload_UTF8_Base64 = mCrypt.Base64Enc(PayLoad.SerializeToJSONUTF8)
    
        http.Open "POST", BitFinexApiURL & RequestSuffix, False
        http.SetRequestHeader "X-BFX-APIKEY", mBitFinexKey
        http.SetRequestHeader "X-BFX-PAYLOAD", sPayload_UTF8_Base64
        http.SetRequestHeader "X-BFX-SIGNATURE", mCrypt.HMAC_SHA384(sPayload_UTF8_Base64, mCrypt.VBStringToUTF8(mBitFinexSecret))
        http.Send
    
        Debug.Print StrConv(http.ResponseBody, vbUnicode)
        
        'If http.Status = 200 Then
        '  Set DoAuthenticatedRPC = New_c.JSONDecodeToCollection(http.ResponseBody)
        'Else
        '  Set DoAuthenticatedRPC = New_c.JSONObject
        'End If
        'DoAuthenticatedRPC.Prop("httpStatus") = http.Status & ": " & http.StatusText

  7. #7
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: Help communicating with web API

    Quote Originally Posted by Max187Boucher View Post
    Hey Olaf ( I am trying to figure it out but no luck so far) I get an Error object variable or with block variable not set
    This is the line
    Code:
    DoAuthenticatedRPC.Prop("httpStatus") = http.Status & ": " & http.StatusText
    Yes, sorry - forgot to give the correct JSON-decoding-method (for UTF8-ByteContent) in the above code:

    Code:
    Set DoAuthenticatedRPC = New_c.JSONDecodeToCollection(http.ResponseBody)
    had to be:

    Code:
    Set DoAuthenticatedRPC = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
    I've corrected that now in the above snippet...
    Your own version, using StrConv would work only for ASCII (should work in most cases, but better to go with the original, corrected method)

    Olaf

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: Help communicating with web API

    It is working 100% so far now Olaf, one last question how do I retrieve the information needed... I can get it all in one line (I guess I can split text/parse it myself) but was wondering if there is a way to call the exact (header/node/element)
    I added
    Code:
    'class
    DoAuthenticatedRPC.Prop("balance") = http.ResponseText
    'form
    Debug.Print Result("balance")
    Code:
    {"type":"deposit","currency":"btc","amount":"0.0","available":"0.0"},{"type":"exchange","currency":"btc","amount":"0.0","available":"0.0"},{"type":"exchange","currency":"usd","amount":"11111111.9999","available":"1111111.9999"}]

  9. #9

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    Thanks to Olaf this is Resolved, we can always count on you Olaf, the help is very appreciated, and thanks for your valuable (free) time!!

  10. #10
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: Help communicating with web API

    Quote Originally Posted by Max187Boucher View Post
    It is working 100% so far now Olaf, one last question how do I retrieve the information needed... I can get it all in one line (I guess I can split text/parse it myself) but was wondering if there is a way to call the exact (header/node/element)
    I added...
    Max, could you please try with an absolutely unchanged cBitFinex-Class (as updated in #3)...

    I've even added a second Public Method DoUnauthenticatedRPC to it now, so that the complete BitFinex-API is covered.

    Maybe some explanations with regards to the Result-cCollection (which encapsulates the JSON-response already
    in a convenient way - including proper UTF8-Unicode-decoding - so that you *don't* have to do any parsing yourself)...

    The BitFinex-API-Description (about the fields in the Responses) is here: https://www.bitfinex.com/pages/api

    Example, how to use the Result-Collection with an Unauthenticated RPC:

    Code:
    Dim BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String
    
    If BitFinex.DoUnauthenticatedRPC("/lendbook/btc", Result, httpStatusText) Then
      
      Dim Bids As cCollection
      Set Bids = Result("bids")
    
      Dim i As Long
      For i = 0 To Bids.Count - 1
        Debug.Print "Arr-Index:"; i, "rate: "; Bids(i)("rate")
        Debug.Print "Arr-Index:"; i, "amount: "; Bids(i)("amount")
        Debug.Print "Arr-Index:"; i, "period: "; Bids(i)("period")
        Debug.Print "Arr-Index:"; i, "timestamp: "; Bids(i)("timestamp")
        Debug.Print "Arr-Index:"; i, "frr: "; Bids(i)("frr")
      Next
    
    Else
      Debug.Print httpStatusText
    End If
    Example, how to use the Result-Collection with Authenticated RPC:

    Code:
    Dim BitFinex As New cBitFinex
        BitFinex.Init "KEY HERE", "SECRET KEY HERE"
    
    Dim Result As cCollection, httpStatusText As String
    
    If BitFinex.DoAuthenticatedRPC("/balances", Result, httpStatusText) Then
      Dim i As Long
      For i = 0 To Result.Count - 1
        Debug.Print "Arr-Index:"; i, "type: "; Result(i)("type")
        Debug.Print "Arr-Index:"; i, "currency: "; Result(i)("currency")
        Debug.Print "Arr-Index:"; i, "amount: "; Result(i)("amount")
        Debug.Print "Arr-Index:"; i, "available: "; Result(i)("available")
      Next i
    Else
      Debug.Print httpStatusText
    End If
    HTH

    Olaf
    Last edited by Schmidt; Apr 18th, 2015 at 09:46 PM. Reason: Changed the examples, to match with the new Class in post #3

  11. #11

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    I will try it in a 15min and post back i was wondering about the unauthenticaded version which i might not use but its good to know thanks again will post back soon

  12. #12

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    I get type mismatch error
    Name:  errr.png
Views: 1206
Size:  7.1 KB

    using
    Code:
    Option Explicit
    
    Const BitFinexApiVersion$ = "/v1", BitFinexApiURL = "https://api.bitfinex.com" & BitFinexApiVersion
    
    Private mBitFinexKey As String, mBitFinexSecret As String, mCrypt As cCrypt
    
    Friend Sub Init(BitFinexKey As String, BitFinexSecret As String)
      mBitFinexKey = BitFinexKey
      mBitFinexSecret = BitFinexSecret
      Set mCrypt = New_c.Crypt
    End Sub
     
    Public Function DoAuthenticatedRPC(RequestSuffix As String, Optional ByVal PayLoad As cCollection) As cCollection
    Dim sPayload_UTF8_Base64 As String, http As New WinHttpRequest
    
     If PayLoad Is Nothing Then Set PayLoad = New_c.JSONObject
        PayLoad.Prop("request") = BitFinexApiVersion & RequestSuffix
        PayLoad.Prop("nonce") = CStr(New_c.Connection.UniqueID64) 'that's a time-based, increasing int64-value
     
        sPayload_UTF8_Base64 = mCrypt.Base64Enc(PayLoad.SerializeToJSONUTF8)
    
        http.Open "POST", BitFinexApiURL & RequestSuffix, False
        http.SetRequestHeader "X-BFX-APIKEY", mBitFinexKey
        http.SetRequestHeader "X-BFX-PAYLOAD", sPayload_UTF8_Base64
        http.SetRequestHeader "X-BFX-SIGNATURE", mCrypt.HMAC_SHA384(sPayload_UTF8_Base64, mCrypt.VBStringToUTF8(mBitFinexSecret))
        http.Send
        
        If http.Status = 200 Then
          Set DoAuthenticatedRPC = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
        Else
          Set DoAuthenticatedRPC = New_c.JSONObject
        End If
        DoAuthenticatedRPC.Prop("httpStatus") = http.Status & ": " & http.StatusText
    End Function
     
    Public Function DoUnauthenticatedRPC(RequestSuffix As String) As cCollection
    Dim http As New WinHttpRequest
        http.Open "GET", BitFinexApiURL & RequestSuffix, False
        http.Send
        
        If http.Status = 200 Then
          Set DoUnauthenticatedRPC = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
        Else
          Set DoUnauthenticatedRPC = New_c.JSONObject
        End If
        DoUnauthenticatedRPC.Prop("httpStatus") = http.Status & ": " & http.StatusText
    End Function

    and using
    Code:
    Private Sub Command1_Click()
    Dim BitFinex As New cBitFinex
        BitFinex.Init "Key", "Secreit Key"
    
    Dim Result As cCollection
    Set Result = BitFinex.DoAuthenticatedRPC("/balances")
    
    If Left(Result("httpStatus"), 3) = "200" Then
      
      Dim i As Long
      For i = 0 To Result.Count - 1
        Debug.Print "Arr-Index:"; i, "type: "; Result(i)("type")
        Debug.Print "Arr-Index:"; i, "currency: "; Result(i)("currency")
        Debug.Print "Arr-Index:"; i, "amount: "; Result(i)("amount")
        Debug.Print "Arr-Index:"; i, "available: "; Result(i)("available")
      Next i
    
    Else
      Debug.Print Result("httpStatus")
    End If
    
    End Sub
    Edit:

    I tried Result("amount") (removed the (i)) and vice versa but still not working.. not working by meaning its blank (the Result() string is blank) but no error
    even tried with on error resume next nothing came out
    Last edited by Max187Boucher; Apr 18th, 2015 at 09:25 PM.

  13. #13

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    I'm getting excited all I need now is to read the chart and make some algorithm that will bid/sell for me, I might open a new thread on how to read the data from your chart example you gave me a few days ago, I know it's probably simple (for you) but a bit of guidance will get me going.

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    got it!!

    Code:
    Debug.Print "Arr-Index:"; i, "type: "; Result.ItemByIndex(i)("type")
    tell me if that suppose to be wrong? because it works and changed to .count - 2 (or else it threw an error because last item did not exist)

    Code:
      Dim i As Long
      For i = 0 To Result.Count - 2
        Debug.Print "Arr-Index:"; i, "type: "; Result.ItemByIndex(i)("type")
        Debug.Print "Arr-Index:"; i, "currency: "; Result.ItemByIndex(i)("currency")
        Debug.Print "Arr-Index:"; i, "amount: "; Result.ItemByIndex(i)("amount")
        Debug.Print "Arr-Index:"; i, "available: "; Result.ItemByIndex(i)("available")
      Next i
    Returned:
    Code:
    Arr-Index: 0  type: deposit
    Arr-Index: 0  currency: btc
    Arr-Index: 0  amount: 0.0
    Arr-Index: 0  available: 0.0
    Arr-Index: 1  type: exchange
    Arr-Index: 1  currency: btc
    Arr-Index: 1  amount: 0.0
    Arr-Index: 1  available: 0.0
    Arr-Index: 2  type: exchange
    Arr-Index: 2  currency: usd
    Arr-Index: 2  amount: ###.###
    Arr-Index: 2  available: ###.###
    yes it returned the amount I changed to ###.###
    Last edited by Max187Boucher; Apr 18th, 2015 at 09:35 PM.

  15. #15
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API

    Quote Originally Posted by Max187Boucher View Post
    I get type mismatch error
    Ok, in case of the "/balances" call, the error happened, because the JSON-result is not returned as a JSON-Object,
    but as a JSON-array - and thus I cannot simply add a Key/Value pair as e.g. my: "httpStatus":"200 OK" to it.
    (Key-Value-Pairs are only "addable" to a JSON-Object-Container, not to a JSON-Array-Container).

    So, I've changed the stuff in post #3 again (for the last time hopefully <g>) -
    and adapted the two examples in post #10 appropriately as well.

    It would be really nice, when you could update the complete Class again (according to #3) -
    and then maybe check the two updated examples in #10 for me too... (so that others will
    find tested stuff in this thread, which will not choke anymore).

    Edit: Just saw what you did in your posting above - that's a workaround which is not
    necessary anymore, when the latest Class from #3 is used (usage then according to #10).

    Olaf

  16. #16

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    How would I get this one Olaf... I tried btcusd, btc, usd,usdbtc, $, :btcusd, :btc.....

    Code:
    GET /pubticker/:symbol
    .......
    If BitFinex.DoUnauthenticatedRPC("/pubticker/usdbtc", Result, httpStatusText) Then
    and I tried your new updated version of Post #3 and Post #10 everything works as expected no changes

  17. #17

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    Ok so I tried your alternative and it returned a json string

    Code:
    If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
      Debug.Print Result.SerializeToJSONString
    Else
      Debug.Print httpStatusText
    End If
    so now I try with the function
    Code:
    Private Sub Command2_Click()
    Dim BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String
    If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
      
      Dim Bids As cCollection
      Set Bids = Result("bids")
    
      Dim i As Long
      For i = 0 To Bids.Count - 1
        Debug.Print "Arr-Index:"; i, "rate: "; Bids(i)("rate")
        Debug.Print "Arr-Index:"; i, "amount: "; Bids(i)("amount")
        Debug.Print "Arr-Index:"; i, "period: "; Bids(i)("period")
        Debug.Print "Arr-Index:"; i, "timestamp: "; Bids(i)("timestamp")
        Debug.Print "Arr-Index:"; i, "frr: "; Bids(i)("frr")
      Next
    
    Else
      Debug.Print httpStatusText
    End If
    End Sub
    says error Object Required
    Name:  err1.jpg
Views: 1170
Size:  26.1 KB


    so if I simply use a string like this

    Code:
    Private Sub Command2_Click()
    Dim BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String
    If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
      Dim s as string
      s = Result("bid")
      Debug.Print "Bid: " & s
    Else
      Debug.Print httpStatusText
    End If
    End Sub
    this works as expected, could we modify our function a little so it can return a simple string or array of strings?

  18. #18
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API

    Quote Originally Posted by Max187Boucher View Post
    How would I get this one Olaf... I tried btcusd, btc, usd,usdbtc, $, :btcusd, :btc.....

    Code:
    GET /pubticker/:symbol
    .......
    If BitFinex.DoUnauthenticatedRPC("/pubticker/usdbtc", Result, httpStatusText) Then
    and I tried your new updated version of Post #3 and Post #10 everything works as expected no changes
    Ok, thanks for testing that...

    As for your question above, any API should be doable now with the Class - so the following is,
    how to use it in that case: according to https://www.bitfinex.com/pages/api - there's only
    normal "Key-Value-Fields" in the JSON-Result-Collection, so we can enumerate the Keys
    and the Values in a Loop...


    Code:
    Dim i As Long, BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String
    
    If BitFinex.DoUnauthenticatedRPC("/symbols", Result, httpStatusText) Then
      Debug.Print vbLf; "Available Symbols:"
      For i = 0 To Result.Count - 1
        Debug.Print Result(i)
      Next
    Else
      Debug.Print httpStatusText
    End If
    
    'now we make use of one of the above listed symbols (btcusd) in the next call
    If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
      Debug.Print vbLf; "Requested Result, calling: /pubticker/btcusd"
      For i = 0 To Result.Count - 1
        Debug.Print Result.KeyByIndex(i), Result.ItemByIndex(i)
      Next
    Else
      Debug.Print httpStatusText
    End If
    Olaf

  19. #19

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    Sorry one last thing never thought about it was too excited haha

    how would I post data to make a new order.. now that we got the request part covered

  20. #20
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API

    Quote Originally Posted by Max187Boucher View Post
    Ok so I tried your alternative and it returned a json string

    Code:
    If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
      Debug.Print Result.SerializeToJSONString
    Else
      Debug.Print httpStatusText
    End If
    That's only the output of the unparsed content of the JSON-Collection
    (which works on any content, and I listed it therefore as the "generic example" in #3).

    Though the whole point of JSON-Parsing-Encapsulations is, that you gain comfort
    whilst reading or writing the contained Fields (in this case over a cCollection).

    Quote Originally Posted by Max187Boucher View Post
    so now I try with the function
    Code:
    Private Sub Command2_Click()
    Dim BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String
    If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
      
      Dim Bids As cCollection
      Set Bids = Result("bids")
    ...
    Max, when you want to work with the result-Collection directly, you have
    to look at "which Named-Fields are in there first".

    You can do that two ways:
    - by enumerating the Keys on the Result-cCollection (per Result.KeyByIndex(i))
    - by looking at the API-Documentation for the call in question on: https://www.bitfinex.com/pages/api

    The latter one is recommended:

    So in case of the call above: /pubticker/btcusd
    The documentation says, that the Response-Result contains the following fields:
    Response:

    Key Type Description
    mid [price] (bid + ask) / 2
    bid [price] Innermost bid.
    ask [price] Innermost ask.
    last_price [price] The price at which the last order executed.
    low [price] Lowest trade price of the last 24 hours
    high [price] Highest trade price of the last 24 hours
    volume [price] Trading volume of the last 24 hours
    timestamp [time] The timestamp at which this information was valid.

    And in case of this call, the Field "bid" is a normal Value-Field (Price), ...

    whereas in the other call you tested: /lendbook/btc
    The documentation says:
    Response

    Key Type Description
    bids [array of loan demands]
    rate [rate in % per 365 days]
    amount [decimal]
    period [days] minimum period for the loan
    timestamp [time]
    frr [yes/no] "Yes" if the offer is at Flash Return Rate, "No" if the offer is at fixed rate
    asks [array of loan offers]
    rate [rate in % per 365 days]
    amount [decimal]
    period [days] maximum period for the loan
    timestamp [time]
    frr [yes/no] "Yes" if the offer is at Flash Return Rate, "No" if the offer is at fixed rate

    Now we have a Field "bids", which is flagged as an array - and array-members are
    contained not as a "direct accessible value-field", but over a different cCollection-
    instance, which you can then "dive into" (one level deeper) and enumerate separately.

    Therefore the example for /lendbook/btc did this:

    Code:
      '... do the call, retrieve the Result-Collection
      Dim Bids As cCollection
      Set Bids = Result("bids")
    So, what we did above, was the retrieval of a "SubObject" of the ResultList "by Name" (Set Bids = Result("bids").

    In this concrete case, there's only one second Member in the Results-Collection, the Field "asks"
    (note the trailing 's' in both cases) - so, to enumerate the contents of Result("asks"), we would
    do a similar thing as we did with Result("bids") - using a temporary Variable of type cCollection,
    to retrieve the Result-Field-Content for the Key "asks", then enumerate its members
    (the documentation is a bit "unsharp" in this case, because the listed Members belong to the
    SubObjects, they are not contained directly in the Result-Collection).

    Olaf

  21. #21

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API

    Quote Originally Posted by Schmidt View Post
    That's only the output of the unparsed content of the JSON-Collection
    (which works on any content, and I listed it therefore as the "generic example" in #3).

    Though the whole point of JSON-Parsing-Encapsulations is, that you gain comfort
    whilst reading or writing the contained Fields (in this case over a cCollection).



    Max, when you want to work with the result-Collection directly, you have
    to look at "which Named-Fields are in there first".

    You can do that two ways:
    - by enumerating the Keys on the Result-cCollection (per Result.KeyByIndex(i))
    - by looking at the API-Documentation for the call in question on: https://www.bitfinex.com/pages/api

    The latter one is recommended:

    So in case of the call above: /pubticker/btcusd
    The documentation says, that the Response-Result contains the following fields:
    Response:

    Key Type Description
    mid [price] (bid + ask) / 2
    bid [price] Innermost bid.
    ask [price] Innermost ask.
    last_price [price] The price at which the last order executed.
    low [price] Lowest trade price of the last 24 hours
    high [price] Highest trade price of the last 24 hours
    volume [price] Trading volume of the last 24 hours
    timestamp [time] The timestamp at which this information was valid.

    And in case of this call, the Field "bid" is a normal Value-Field (Price), ...

    whereas in the other call you tested: /lendbook/btc
    The documentation says:
    Response

    Key Type Description
    bids [array of loan demands]
    rate [rate in % per 365 days]
    amount [decimal]
    period [days] minimum period for the loan
    timestamp [time]
    frr [yes/no] "Yes" if the offer is at Flash Return Rate, "No" if the offer is at fixed rate
    asks [array of loan offers]
    rate [rate in % per 365 days]
    amount [decimal]
    period [days] maximum period for the loan
    timestamp [time]
    frr [yes/no] "Yes" if the offer is at Flash Return Rate, "No" if the offer is at fixed rate

    Now we have a Field "bids", which is flagged as an array - and array-members are
    contained not as a "direct accessible value-field", but over a different cCollection-
    instance, which you can then "dive into" (one level deeper) and enumerate separately.

    Therefore the example for /lendbook/btc did this:

    Code:
      '... do the call, retrieve the Result-Collection
      Dim Bids As cCollection
      Set Bids = Result("bids")
    So, what we did above, was the retrieval of a "SubObject" of the ResultList "by Name" (Set Bids = Result("bids").

    In this concrete case, there's only one second Member in the Results-Collection, the Field "asks"
    (note the trailing 's' in both cases) - so, to enumerate the contents of Result("asks"), we would
    do a similar thing as we did with Result("bids") - using a temporary Variable of type cCollection,
    to retrieve the Result-Field-Content for the Key "asks", then enumerate its members
    (the documentation is a bit "unsharp" in this case, because the listed Members belong to the
    SubObjects, they are not contained directly in the Result-Collection).

    Olaf
    That makes sense, now I know how to retrieve arrays inside the json object (with your examples)
    So far I got everything working except not sure about the resquest (to post data TO the website) I'm thinking with your extra parameter Payload... so I just do this

    quick example of what I think it should be
    Code:
    Dim col As cCollection
    
    col.Prop("symbol") = "btcusd"
    col.Prop("amount") = 0.1 'would this still be a string it says decimal?
    '.....
    If BitFinex.DoAuthenticatedRPC("/balances", Result, httpStatusText, col) Then
    '.......
    Edit:
    Tried this

    Code:
    If PayLoad Is Nothing Then Set PayLoad = New_c.JSONObject
        PayLoad.Prop("request") = BitFinexApiVersion & RequestSuffix
        PayLoad.Prop("nonce") = CStr(New_c.Connection.UniqueID64) 'that's a time-based, increasing int64-value
        PayLoad.Prop("symbol") = "btcusd"
        PayLoad.Prop("amount") = "CDec(0.1)"
        PayLoad.Prop("price") = "222"
        PayLoad.Prop("exchange") = "bitfinex"
        PayLoad.Prop("side") = "buy"
        PayLoad.Prop("type") = "exchange limit"
        PayLoad.Prop("is_hidden") = 0
        sPayload_UTF8_Base64 = mCrypt.Base64Enc(PayLoad.SerializeToJSONUTF8)
    '........................
    bad request
    Last edited by Max187Boucher; Apr 19th, 2015 at 12:00 AM.

  22. #22
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API

    Quote Originally Posted by Max187Boucher View Post
    So far I got everything working except not sure about the resquest (to post data TO the website) I'm thinking with your extra parameter Payload... so I just do this

    quick example of what I think it should be
    Code:
    Dim col As cCollection
    
    col.Prop("symbol") = "btcusd"
    col.Prop("amount") = 0.1 'would this still be a string it says decimal?
    '.....
    If BitFinex.DoAuthenticatedRPC("/balances", Result, httpStatusText, col) Then
    '.......

    That's (basically) the right approach - meaning, the Methods in the Class should be used
    generically (for any authenticated ot unauthenticated API) - and if Parameters are
    requried, then those should be provided from outside (as in your snippet above) -
    and not directly manipulated within the generic functions themselves...

    Well, that said - and from what I think you want to attempt (a new order, right?) -
    the Documentation says:
    New order

    Submit a new order.
    POST /order/new

    Request

    Key Type Description
    symbol [string] The name of the symbol (see `/symbols`).
    amount [decimal] Order size: how much to buy or sell.
    price [price] Price to buy or sell at. Must be positive. Use random number for market orders.
    exchange [string] "bitfinex"
    side [string] Either "buy" or "sell".
    type [string] Either "market" / "limit" / "stop" / "trailing-stop" / "fill-or-kill" / "exchange market" / "exchange limit" / "exchange stop" / "exchange trailing-stop" / "exchange fill-or-kill". (type starting by "exchange " are exchange orders, others are margin trading orders)
    is_hidden [bool] true if the order should be hidden. Default is false.

    So, the first thing to check, would be the correct Request-Suffix for the call, which in this case would be the Literal:
    "/order/new"
    which needs to be passed...

    Maybe that was the only thing you overlooked...

    Here's how I would try it:

    Code:
    Dim BitFinex As New cBitFinex
        BitFinex.Init "KEY HERE", "SECRET KEY HERE"
    
    Dim Args As cCollection
    Set Args = New_c.JSONObject
        Args.Prop("symbol") = "btcusd"
        Args.Prop("amount") = "0.1" 'pass it as string
        Args.Prop("price") = "222"
        Args.Prop("exchange") = "bitfinex"
        Args.Prop("side") = "buy"
        Args.Prop("type") = "exchange limit"
        Args.Prop("is_hidden") = False
    Debug.Print Args.SerializeToJSONString '<-Just for control-purposes, before we pass it
     
    Dim Result As cCollection, httpStatusText As String
    If BitFinex.DoAuthenticatedRPC("/order/new", Result, httpStatusText, Args) Then
      Debug.Print Result("order_id")
    Else
      Debug.Print httpStatusText
    End If
    Olaf

  23. #23

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    the is_hidden prop has to be true or else be excluded from the collection (because default is false, for some reason they don't accept false as a property but True works).... I got it to work properly now
    Thanks Olaf I would not have made it without you that for sure.

    Works
    Code:
    BitFinex.Init "key", "secret key"
    Dim Args As cCollection
    Set Args = New_c.JSONObject
        Args.Prop("symbol") = "btcusd"
        Args.Prop("amount") = "0.05" 'pass it as string
        Args.Prop("price") = "222.2"
        Args.Prop("exchange") = "bitfinex"
        Args.Prop("side") = "buy"
        Args.Prop("type") = "exchange limit"
        Args.Prop("is_hidden") = True
    Dim Result As cCollection, httpStatusText As String
    If BitFinex.DoAuthenticatedRPC("/order/new", Result, httpStatusText, Args) Then
      Debug.Print Result("order_id")
    Else
      Debug.Print httpStatusText
    End If
     
    
    'OR
    
    BitFinex.Init "key", "secret key"
    Dim Args As cCollection
    Set Args = New_c.JSONObject
        Args.Prop("symbol") = "btcusd"
        Args.Prop("amount") = "0.05" 'pass it as string
        Args.Prop("price") = "222.2"
        Args.Prop("exchange") = "bitfinex"
        Args.Prop("side") = "buy"
        Args.Prop("type") = "exchange limit"
    Dim Result As cCollection, httpStatusText As String
    If BitFinex.DoAuthenticatedRPC("/order/new", Result, httpStatusText, Args) Then
      Debug.Print Result("order_id")
    Else
      Debug.Print httpStatusText
    End If
    Edit:
    Here is a little quick function I made, thought I'd post it just for the heck of it... the more examples the better!

    Form code
    Code:
    Private Function Create_NewOrder(Optional sSymbol As String = "btcusd", _
                                     Optional sAmount As String = "0.05", _
                                     Optional sPrice As String = "222", _
                                     Optional sExchange As String = "bitfinex", _
                                     Optional sSide As String = "buy", _
                                     Optional sType As String = "exchange limit", _
                                     Optional bIsHidden As Boolean = False)
    Dim BitFinex As New cBitFinex
        BitFinex.Init "key", "secret key"
    
    Dim Args As cCollection
    
    Set Args = New_c.JSONObject
        Args.Prop("symbol") = sSymbol
        Args.Prop("amount") = sAmount
        Args.Prop("price") = sPrice
        Args.Prop("exchange") = sExchange
        Args.Prop("side") = sSide
        Args.Prop("type") = sType
        If bIsHidden Then Args.Prop("is_hidden") = bIsHidden
        
    Dim Result As cCollection, httpStatusText As String
    If BitFinex.DoAuthenticatedRPC("/order/new", Result, httpStatusText, Args) Then
      Debug.Print Result("order_id")
    Else
      Debug.Print httpStatusText
    End If
    End Function
    With the class from Post#3
    Code:
    Option Explicit
    
    Const BitFinexApiVersion$ = "/v1", BitFinexApiURL = "https://api.bitfinex.com" & BitFinexApiVersion
    
    Private mBitFinexKey As String, mBitFinexSecret As String, mCrypt As cCrypt
    
    Friend Sub Init(BitFinexKey As String, BitFinexSecret As String)
      mBitFinexKey = BitFinexKey
      mBitFinexSecret = BitFinexSecret
      Set mCrypt = New_c.Crypt
    End Sub
     
    Public Function DoAuthenticatedRPC(RequestSuffix As String, Result As cCollection, Optional httpStatusText As String, Optional ByVal PayLoad As cCollection) As Boolean
    Dim sPayload_UTF8_Base64 As String, http As New WinHttpRequest
    
     If PayLoad Is Nothing Then Set PayLoad = New_c.JSONObject
        PayLoad.Prop("request") = BitFinexApiVersion & RequestSuffix
        PayLoad.Prop("nonce") = CStr(New_c.Connection.UniqueID64) 'that's a time-based, increasing int64-value
     
        sPayload_UTF8_Base64 = mCrypt.Base64Enc(PayLoad.SerializeToJSONUTF8)
    
        http.Open "POST", BitFinexApiURL & RequestSuffix, False
        http.SetRequestHeader "X-BFX-APIKEY", mBitFinexKey
        http.SetRequestHeader "X-BFX-PAYLOAD", sPayload_UTF8_Base64
        http.SetRequestHeader "X-BFX-SIGNATURE", mCrypt.HMAC_SHA384(sPayload_UTF8_Base64, mCrypt.VBStringToUTF8(mBitFinexSecret))
        http.Send
        
        httpStatusText = http.Status & ": " & http.StatusText
        If http.Status <> 200 Then Exit Function
        Set Result = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
        DoAuthenticatedRPC = True
    End Function
     
    Public Function DoUnauthenticatedRPC(RequestSuffix As String, Result As cCollection, Optional httpStatusText As String, Optional ByVal PayLoad As cCollection) As Boolean
    Dim http As New WinHttpRequest
        http.Open "GET", BitFinexApiURL & RequestSuffix, False
        http.Send
        
        httpStatusText = http.Status & ": " & http.StatusText
        If http.Status <> 200 Then Exit Function
        Set Result = New_c.JSONDecodeToCollectionUTF8(http.ResponseBody)
        DoUnauthenticatedRPC = True
    End Function
    Last edited by Max187Boucher; Apr 19th, 2015 at 02:13 AM. Reason: Noticed I had left my API key and secret key... I removed it, and changed them on bitfinex :) just for safety

  24. #24
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Glad you have it working also with Parameter-calls (in Authenticated Mode).

    For completeness, I've updated the Base-Class in post #3 one last time
    (this time just a small enhancement on the Unauthenticated Method, which
    now supports Parameter-Passing as well - in the same way as the other method)

    Here's a usage example:

    Code:
    Dim BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String, Args As cCollection
    
    'first without the optional Arguments (which gives back the arrays in their full length)
    If BitFinex.DoUnauthenticatedRPC("/lendbook/btc", Result, httpStatusText) Then
      Debug.Print "Array-Length of bids:", Result("bids").Count
      Debug.Print "Array-Length of asks:", Result("asks").Count
    Else
      Debug.Print httpStatusText
    End If
    
    'now the same call again, but providing the two limiting-parameters
    Set Args = New_c.JSONObject
        Args.Prop("limit_bids") = 5
        Args.Prop("limit_asks") = 5
    
    If BitFinex.DoUnauthenticatedRPC("/lendbook/btc", Result, httpStatusText, Args) Then
      Debug.Print "Array-Length of bids:", Result("bids").Count
      Debug.Print "Array-Length of asks:", Result("asks").Count
    Else
      Debug.Print httpStatusText
    End If
    Prints out:
    Code:
    Array-Length of bids:       24 
    Array-Length of asks:       50 
    
    Array-Length of bids:        5 
    Array-Length of asks:        5
    I think with that last addition it is now a quite usable encapsulation -
    and in your shoes I'd probably implement an additional small class,
    wrapper-functions with a "proper name and specialized parameters" ->
    one for each request-string in the Bitfinex-API you plan to use often.

    E.g. the snippet above could then look something like:

    Code:
    Public Function GetLendBook(ResBids as cCollection, ResAsks as cCollection, Optional CurrencyString$="btc", Optional LimitBids, Optional LimitAsks)
    Dim Result As cCollection, httpStatusText As String, Args As cCollection
      Set Args = New_c.JSONObject
      If Not IsMissing(LimitBids) then Args.Prop("limit_bids") = LimitBids
      If Not IsMissing(LimitAsks) then Args.Prop("limit_asks") = LimitAsks
     
      If BitFinex.DoUnauthenticatedRPC("/lendbook/" & CurrencyString, Result, httpStatusText, Args) Then
        GetLendBook = True
        Set ResBids = Result("bids") 
        Set ResAsks= Result("asks")
      Else
        LogError  "GetLendBook: " & httpStatusText
      End If
    End Function
    a.s.o. for the other functions you plan to use from that API...

    Edit: Have seen now, that you already started with such Function Wrappers in your post above...
    Way to go...


    Olaf
    Last edited by Schmidt; Apr 19th, 2015 at 03:30 AM.

  25. #25

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Thanks Olaf you've done more than enough for now, if I need more help on this which I doubt now I'll come back here and ask for help.
    I will be working on the charts probably at the end of the week I see you had high/low in your chart I'll need to perform some algo to so I can try and hedge between certain prices to minimize loses and maximize profit

    To be honest I looked and looked never found anything in vb6 code it was vb.net the closest but still was missing utf8/base64/sha384 and authenticate.... I was getting closer but would have never figured it out probably without you...

    I hope some people will use this functional class if they can!

  26. #26

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Hey Olaf (or anyone that knows) I am running into a TimeStamp problem giving wrong date or order...

    Code:
    Public Function ViewOrders() As String
    Dim i As Integer
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
      
      BitFinex.Init sKey, sKeySecret
      
      If BitFinex.DoAuthenticatedRPC("/orders", Result, httpStatusText) Then
          For i = 0 To Result.Count - 1
            Debug.Print Result.Item(i)("id"), Result.Item(i)("side"), Result(i)("price"), Result(i)("original_amount"), "TimeStamp: " & Result(i)("timestamp") & "  -  Date: " & DateAdd("s", Result(i)("timestamp"), #1/1/1970#)
          Next
      Else
          Debug.Print httpStatusText
      End If
    End Function
    This is what is returned in my debug window... but its the wrong date I placed the order on the 4/18/(around maybe 10pm)
    Code:
     244640779    buy           222.5         0.05          TimeStamp: 1429419783.0  -  Date: 4/19/2015 5:03:03 AM
     244640943    buy           221.5         0.05          TimeStamp: 1429419807.0  -  Date: 4/19/2015 5:03:27 AM
     244641005    buy           222.8         0.05          TimeStamp: 1429419814.0  -  Date: 4/19/2015 5:03:34 AM
     244650570    buy           221.0         0.05          TimeStamp: 1429421290.0  -  Date: 4/19/2015 5:28:10 AM
     244650600    buy           220.0         0.05          TimeStamp: 1429421293.0  -  Date: 4/19/2015 5:28:13 AM
     244995675    buy           224.0         0.05          TimeStamp: 1429471134.0  -  Date: 4/19/2015 7:18:54 PM
    Name:  err4.jpg
Views: 1093
Size:  5.3 KB
    Last edited by Max187Boucher; Apr 19th, 2015 at 02:58 PM.

  27. #27
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Well, these "UnixEpoch"-Timestamps are apparently in UTC.

    So, simply adding the Unix-StartDate will not be enough.

    SQLite for example has a nice built-in function-set for this kind of time-values -
    and so you won't need to write a special converter-routine yourself - especially
    not, since I'd store the retrieved WebResults in an little InMemory-DB anyways
    (for easier querying later on).

    Here's a small example, that shows how you can "upload" incoming values
    into a DB-Table, followed by a query, which gives back "localized Dates"
    from your UTC-UnixEpoch-TimeStamp-values.

    Code:
    Private Sub Form_Load()
    Dim MemDB As cMemDB
    Set MemDB = New_c.MemDB
        MemDB.Exec "Create Table T(BitTimesUTC Double)"
       
      'upload of a few Demo-Values into the MemDB-Table
      With MemDB.GetRs("Select * From T Where 1=0")
          .AddNew: !BitTimesUTC = 1429419783
          .AddNew: !BitTimesUTC = 1429419807
          .AddNew: !BitTimesUTC = 1429419814
          .AddNew: !BitTimesUTC = 1429421290
          .AddNew: !BitTimesUTC = 1429421293
          .AddNew: !BitTimesUTC = 1429471134
        .UpdateBatch
      End With
     
     'now the read-direction, using SQLites DateTime-function for the conversion into localtime
      With MemDB.GetRs("Select DateTime(BitTimesUTC, 'unixepoch', 'localtime') As BitTimesLOC From T")
        Do Until .EOF
          Debug.Print CDate(!BitTimesLOC)
          .MoveNext
        Loop
      End With
    End Sub
    Here's the documentation for the Date- and Time-Functions in SQLite:
    http://www.sqlite.org/lang_datefunc.html

    Olaf

  28. #28

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by Schmidt View Post
    Well, these "UnixEpoch"-Timestamps are apparently in UTC.

    So, simply adding the Unix-StartDate will not be enough.

    SQLite for example has a nice built-in function-set for this kind of time-values -
    and so you won't need to write a special converter-routine yourself - especially
    not, since I'd store the retrieved WebResults in an little InMemory-DB anyways
    (for easier querying later on).

    Here's a small example, that shows how you can "upload" incoming values
    into a DB-Table, followed by a query, which gives back "localized Dates"
    from your UTC-UnixEpoch-TimeStamp-values.

    Code:
    Private Sub Form_Load()
    Dim MemDB As cMemDB
    Set MemDB = New_c.MemDB
        MemDB.Exec "Create Table T(BitTimesUTC Double)"
       
      'upload of a few Demo-Values into the MemDB-Table
      With MemDB.GetRs("Select * From T Where 1=0")
          .AddNew: !BitTimesUTC = 1429419783
          .AddNew: !BitTimesUTC = 1429419807
          .AddNew: !BitTimesUTC = 1429419814
          .AddNew: !BitTimesUTC = 1429421290
          .AddNew: !BitTimesUTC = 1429421293
          .AddNew: !BitTimesUTC = 1429471134
        .UpdateBatch
      End With
     
     'now the read-direction, using SQLites DateTime-function for the conversion into localtime
      With MemDB.GetRs("Select DateTime(BitTimesUTC, 'unixepoch', 'localtime') As BitTimesLOC From T")
        Do Until .EOF
          Debug.Print CDate(!BitTimesLOC)
          .MoveNext
        Loop
      End With
    End Sub
    Here's the documentation for the Date- and Time-Functions in SQLite:
    http://www.sqlite.org/lang_datefunc.html

    Olaf
    Quick question on your MemDB, Olaf, does it clean itself upon closing the app? Or do I need to set it to = nothing to clear it up from memory?


    Edit:

    So far, I only need the date/time and your DB example does that so I decided to do it like this for the moment until I learn more about SQL.

    Here is my modified version

    Code:
    Private Function TimeStampDate(ByVal sTimeStamp As Double) As Date
    Dim MemDB As cMemDB
    Set MemDB = New_c.MemDB
        MemDB.Exec "Create Table T(BitTimesUTC Double)"
      With MemDB.GetRs("Select * From T Where 1=0")
          .AddNew: !BitTimesUTC = sTimeStamp
        .UpdateBatch
      End With
    With MemDB.GetRs("Select DateTime(BitTimesUTC, 'unixepoch', 'localtime') As BitTimesLOC From T")
        TimeStampDate = CDate(!BitTimesLOC)
      End With
      Set MemDB = Nothing
    End Function
    Works like a charm!
    Last edited by Max187Boucher; Apr 19th, 2015 at 10:32 PM.

  29. #29
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by Max187Boucher View Post
    Quick question on your MemDB, Olaf, does it clean itself upon closing the app? Or do I need to set it to = nothing to clear it up from memory?
    Upon Process-Termination, any memory-allocation which happened inside a Process is cleaned up of course.

    During Runtime, there's an (SQLite-library-internal) Cache-allocation of a few Megabyte (8MB or so initially),
    and this cache is re-used by new upstarting SQLite-MemDBs (or -FileDBs) - but if you insert 100000
    records into 10 different MemDB-Tables each, then these 8MB initial Memory within the SQLite-lib will
    of course grow (all that data has to sit somewhere) - but its consumption is comparably lean, due to UTF8-
    encoding (less than in an ADO-Recordset for example, where Strings are stored with 16Bit-per-WChar).

    You might want to test the Mem-Consumption (and the proper freeing) with this
    code-snippet (slightly varying from the example above) - where I create (and destroy)
    a MemDB 10 times in a loop (inserting 10000 Records in each of the 10 Runs)...

    Maybe compile and take a look over the TaskManager... The Mem-consumption should
    remain constant, no matter how often you click the Form (in my test it swung itself in
    at roughly 14MB - no Handle- or other leakage observable).

    Code:
    Option Explicit
    
    Private Sub Form_Click()
      Dim i As Long
      For i = 1 To 10
        TestMemDB
        Caption = i
      Next i
      MsgBox "Test-Method finished (10 times create, fill, destroy of a MemDB-instance)"
    End Sub
    
    Private Sub TestMemDB()
    Dim MemDB As cMemDB
    Set MemDB = New_c.MemDB
        MemDB.Exec "Create Table T(BitTimesUTC Double)"
      
      Dim i As Long, StartSecond As Double
      StartSecond = 1429419783
      With MemDB.GetRs("Select * From T Where 1=0")
        For i = 1 To 10000
          .AddNew: .Fields(0).Value = StartSecond + 1
        Next
        .UpdateBatch
      End With
    End Sub 'at this point, the MemDB-Obj is going out of scope and internally cleaned up
    I use MemDBs quite often in my own Apps - and was never biten...
    (a small worst-case calculation, with the maximum expected amount of records
    times average ColumnCount over all tables you might create, will help... as e.g.:

    If you plan to use about 10 tables, with an average of 10 Columns each
    and an average recordcount of 20000 records each, then the calculation is:

    10 * 10 * 20000 * 80Bytes on average per Field... so this amounts to 160MB roughly -
    if you plan to hold that same amount of data in e.g. UDT-Arrays instead, then it is
    questionable whether it is lower in total (mem-allocation-wise) - because the same thing
    I mentioned already with ADO-Rs' applies also for VBs-UDTs (or Arrays of Type String)
    UTF8-based String-Storage will take up (usually) only half the storage of UTF-16.
    (and String-Fields make always a good amount of the Fields in most scenarios).

    What's also nice (in case you start-out with a MemDB from the very beginning,
    instead of using Arrays or other things) - you can write a MemDB-instance with
    one line of code (MemDB.Cnn.CopyDataBase) into a SingleFile-DB in the FileSystem
    (including all Created Tables, Indexes and Contents of your MemDB) - this way
    your very own "Single-File-Document-Format" for your whole Application-State
    (or other things) is always only "one line of code away".

    Nearly forgotten...
    And no, there's no Close-Method (neither on a normal SQLite-cConnection,
    nor on a cRecordset - and also not on a cMemDB - which is just a convenience-wrapper
    around a normal cConnection) - all instances clean themselves up internally, when
    they go out of scope as the COM-Objects they are - (when the last reference-variable
    which points to them, is set to nothing)...


    Olaf
    Last edited by Schmidt; Apr 19th, 2015 at 11:11 PM.

  30. #30

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Thanks for the information I will keep in mind if I use a MemDB...

    So far this is what I made... works good so far


    Module Code:
    Code:
    Option Explicit
    
    
    Const sKey As String = "Key Here"
    Const sKeySecret As String = "Secret Key Here"
    
    Public Enum enumTicker
      Mid = 0
      Bid = 1
      Ask = 2
      LastPrice = 3
      low = 4
      high = 5
      Volume = 6
    End Enum
    
    
    
    Public Function Ticker(GetTickerPrice As enumTicker) As String
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
    
      If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
          Select Case GetTickerPrice
            Case 0
              Ticker = Format(Result("mid"), "0.00")
            Case 1
              Ticker = Format(Result("bid"), "0.00")
            Case 2
              Ticker = Format(Result("ask"), "0.00")
            Case 3
              Ticker = Format(Result("last_price"), "0.00")
            Case 4
              Ticker = Format(Result("low"), "0.00")
            Case 5
              Ticker = Format(Result("high"), "0.00")
            Case 6
              Ticker = Format(Result("volume"), "0.00")
          End Select
      Else
          Ticker = httpStatusText
      End If
    End Function
    
    
    Public Function NewOrder(Price As String, Amount As String, BuyOrSell As String, Optional sType As String = "exchange limit", Optional sSymbol As String = "btcusd", Optional sExchange As String = "bitfinex", Optional bIsHidden As Boolean = False) As String
    Dim Args As cCollection
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
    
      BitFinex.Init sKey, sKeySecret
      
      Set Args = New_c.JSONObject
          Args.Prop("symbol") = sSymbol
          Args.Prop("amount") = Amount
          Args.Prop("price") = Price
          Args.Prop("exchange") = sExchange
          Args.Prop("side") = LCase(BuyOrSell)
          Args.Prop("type") = LCase(sType)
          If bIsHidden Then Args.Prop("is_hidden") = bIsHidden
          
      If BitFinex.DoAuthenticatedRPC("/order/new", Result, httpStatusText, Args) Then
        NewOrder = Result("order_id")
      Else
        NewOrder = httpStatusText
      End If
    End Function
    
    
    Public Function UpdateOrders(OrderList As ListBox) As String
    Dim i As Integer
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
      
      BitFinex.Init sKey, sKeySecret
       
      If BitFinex.DoAuthenticatedRPC("/orders", Result, httpStatusText) Then
          OrderList.Clear
          For i = 0 To Result.Count - 1
              OrderList.AddItem Result.Item(i)("id") & vbTab & _
                                Result.Item(i)("side") & vbTab & _
                                Result(i)("price") & vbTab & _
                                Round(Result(i)("original_amount"), 2) & vbTab & _
                                Format(TimeStampDate(Result(i)("timestamp")), "mmm dd, yyyy   @  hh:mm ampm")
          Next
          UpdateOrders = "Successful"
      Else
          UpdateOrders = httpStatusText
      End If
    End Function
    
    
    Private Function TimeStampDate(ByVal sTimeStamp As Double) As Date
    Dim MemDB As cMemDB
    Set MemDB = New_c.MemDB
        MemDB.Exec "Create Table T(BitTimesUTC Double)"
      With MemDB.GetRs("Select * From T Where 1=0")
          .AddNew: !BitTimesUTC = sTimeStamp
        .UpdateBatch
      End With
    With MemDB.GetRs("Select DateTime(BitTimesUTC, 'unixepoch', 'localtime') As BitTimesLOC From T")
        TimeStampDate = CDate(!BitTimesLOC)
      End With
    End Function
    
    
    Public Function GetOrderInfo(sOrderID As String) As String
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
    Dim OrderID As cCollection
    
        BitFinex.Init sKey, sKeySecret
        
        Set OrderID = New_c.JSONObject
            OrderID.Prop("order_id") = Val(sOrderID)
        
        If BitFinex.DoAuthenticatedRPC("/order/status", Result, httpStatusText, OrderID) Then
          Debug.Print Result.Item("is_live")
          Debug.Print Result.Item("price")
          Debug.Print Result.Item("original_amount")
        Else
          GetOrderInfo = httpStatusText
        End If
    End Function
    
    
    Public Function CancelOrder(Optional sOrderID As String, Optional CancelAll As Boolean = False)
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
    Dim OrderID As cCollection
    
        BitFinex.Init sKey, sKeySecret
        
        If CancelAll Then
            BitFinex.DoAuthenticatedRPC "/order/cancel/all", Result, httpStatusText, OrderID
        Else
            Set OrderID = New_c.JSONObject
                OrderID.Prop("order_id") = Val(sOrderID)
            BitFinex.DoAuthenticatedRPC "/order/cancel", Result, httpStatusText, OrderID
        End If
        CancelOrder = httpStatusText
    End Function
    Form Code:
    Code:
    Option Explicit
    
    
    Private Sub cmdBuy_Click()
    NewOrder txtPrice.Text, txtAmount.Text, "buy", cmbOrder.Text
    End Sub
    
    Private Sub cmdSell_Click()
    NewOrder txtPrice.Text, txtAmount.Text, "sell", cmbOrder.Text
    End Sub
    
    Private Sub Command1_Click()
    'Cancel Single Order
      CancelOrder Left(lstOrders.List(lstOrders.ListIndex), 9), True
    'Cancel All Orders
      CancelOrder , True
    End Sub
    
    Private Sub Command2_Click()
    UpdateOrders lstOrders
    End Sub
    
    Private Sub lstOrders_Click()
    GetOrderInfo Left(lstOrders.List(lstOrders.ListIndex), 9)
    End Sub
    
    Private Sub Timer1_Timer()
    Static LastMid As String
    Dim sTicker As String
    
      sTicker = Ticker(Mid)
      
      If LastMid <> sTicker Then
        LastMid = sTicker
        lstPrice.AddItem sTicker
      End If
      
      lblLow.Caption = Ticker(low)
      lblHigh.Caption = Ticker(high)
      
      UpdateOrders lstOrders
    End Sub
    and of course the Magical Olaf Class in Post #3

    Of course I got lots to do still if anyone is interested at least I added a couple examples to play with(array/nonarray)
    Last edited by Max187Boucher; Apr 19th, 2015 at 11:25 PM.

  31. #31
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by Max187Boucher View Post
    So far this is what I made... works good so far
    Yep, nice code...

    Just one last suggestion - when you use a Class-Encapsulation instead of the
    Module-Code you have so far, you could spare yourself the BitFinex-
    instantiation (and initialization with the two Keys) in each Function-Call,
    by just doing that in Class_Initialize...

    E.g. when you name it cBitFinexWrap - and then place a global Variable as this...

    ...in a Module:
    Code:
    Public gBF As New cBitFinexWrap
    And this within the Class-Wrapper cBitFinexWrap:
    Code:
    Private BitFinex As New cBitFinex 'declaration at Class-Level
    
    Private Sub Class_Initialize()
      BitFinex.Init "MyKey", "MyKeySecret" 'direct initialization at Class-Startup
    End Sub
    Then you can remove all Procedure-local BitFinex-Class-inits (and declarations)
    within your implementations in that Class.

    Olaf

  32. #32

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Not sure why I didn't think about that one i'll remove them next time i work on it

  33. #33
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by Max187Boucher View Post
    Not sure why I didn't think about that one i'll remove them next time i work on it
    Just noticed (after a closer look) that you're using the MemDB in a converter-function
    which works at the "single-value-level".

    That's quite inefficient, to create a MemDB-instance (and a table etc.) for each little conversion.

    If you want to use it this way further, I'd at least change it to:

    Code:
    Private Function TimeStampDate(ByVal sTimeStamp As Double) As Date
    Static MemDB As cMemDB, Cmd As cSelectCommand
        If MemDB Is Nothing Then
          Set MemDB = New_c.MemDB
          Set Cmd = MemDB.CreateSelectCommand("Select DateTime(?, 'unixepoch', 'localtime')")
        End If
     
        Cmd.SetDouble 1, sTimeStamp 'fill the first question-mark-slot with the proper value
        TimeStampDate = CDate(Cmd.Execute.Fields(0).Value)
    End Function
    Which then works about 12 times as fast, since it avoids the instancing on every call -
    and doesn't create any Table anymore (the SQLite-SQL parser can be used entirely
    without any DB-Tables to evaluate simple expressions or functions).

    You currently use this function in your:
    Code:
    Public Function UpdateOrders(OrderList As ListBox) As String
    A better (and much faster, as well as elegant way) would be, to use the MemDB in
    Table-Mode again, and first beam up all your values into it (in your result-loop).

    Then you can select a Recordset from the Table and update e.g. an MSHFlexGrid directly in one line
    (instead of adding items into a VB.ListBox)...

    Here's some TestCode, which can run in a separate VB-TestProject
    (just ensure, it has a Form with an MSHFlexGrid1 on it - and the RC5-reference,
    the Bitfinex-parts were commented out for this test)

    Code:
    Option Explicit
     
    Private Sub Form_Load()
      UpdateOrders MSHFlexGrid1
                   MSHFlexGrid1.ColWidth(4) = 1400
                   MSHFlexGrid1.ColWidth(5) = 2200
    End Sub
    
    Public Function UpdateOrders(OrderList As MSHFlexGrid) As String
    Dim i As Integer
    Dim Result As cCollection, Item As cCollection
    Dim httpStatusText As String
    'Dim BitFinex As New cBitFinex
    
    Static MemDB As cMemDB
        If MemDB Is Nothing Then Set MemDB = New_c.MemDB
    
    '    BitFinex.Init sKey, sKeySecret
         
    '    If BitFinex.DoAuthenticatedRPC("/orders", Result, httpStatusText) Then
            
            '**** just to simulate a response from the server instead of the real thing
              Set Result = New_c.JSONArray
                  Result.Prop(0) = New_c.JSONObject
                  Result.Prop(0).Prop("id") = "id 1"
                  Result.Prop(0).Prop("side") = "side 1"
                  Result.Prop(0).Prop("price") = "1234.56"
                  Result.Prop(0).Prop("original_amount") = "12345.6789"
                  Result.Prop(0).Prop("timestamp") = "1429419783"
            '**** end of result-simulation
            
            MemDB.Exec "Drop Table If Exists T" 'delete the table and the values it might contain from the last round
            MemDB.Exec "Create Table T(id Text, side Text, price Text, original_amount Text, timestamp Text)" 'all fields to Text, to keep it simple
            
            With MemDB.CreateCommand("Insert Into T Values(?,?,?,?,?)") '5 "questionmark-slots", one for each Field in the Table
    
                For i = 0 To Result.Count - 1
                    Set Item = Result.Item(i) 'resolve the Collection-Sub-Object once, before accessing its Fields
                  
                    .SetText 1, Item("id")
                    .SetText 2, Item("side")
                    .SetText 3, Item("price")
                    .SetText 4, Item("original_amount")
                    .SetText 5, Item("timestamp")
                    
                    .Execute 'write the current record-content (our 5 fields we just placed in the slot-numbers) into the Table T
                Next
              
            End With
            
            Const SQL$ = "Select id, side, price, Round(original_amount, 2) As original_amount, " & _
                         "Format$(DateTime(timestamp, 'unixepoch','localtime'), 'mmm dd, yyyy  @  hh:mm AM/PM') As timestamp From T"
            
            Set OrderList.DataSource = MemDB.GetRs(SQL).DataSource 'now update the HFlexGrid with all the records in one go
            
            UpdateOrders = "Successful"
            
    '    Else
    '        UpdateOrders = httpStatusText
    '    End If
    End Function
    Here's a screenshot of the result:



    Since I'm at it (but that's just a matter of taste, not performance-relevant or something)
    For those who have a more "horizontal coding-style" like me (some prefer it more vertically <g>)
    the usage of the Choose-Function is sometimes handy, to resolve from Enum-Values to String-representations:

    Code:
    Public Function Ticker(TickerPrice As enumTicker) As String
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim BitFinex As New cBitFinex
    
      If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
          Ticker = Format(Result(Choose(TickerPrice + 1, "mid", "bid", "ask", "last_price", "low", "high", "volume")), "0.00")
      Else
          Ticker = httpStatusText
      End If
    End Function
    But as said, that's only cosmetic stuff, which might or might not be to someones personal taste...


    Olaf
    Last edited by Schmidt; Apr 20th, 2015 at 02:05 AM.

  34. #34

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Ok so I haven't have any time to work on it... except maybe an hour, but I modified a couple little things and added it to my old (GUI) version of my app. This way you can have a look and if anyone is interested they can check it out (not every function works, it used to but not with the API )

    I am not good with DB stuff, specially memDB so I'm not sure if I did it correctly but its working.
    I will update it eventually

    Next step will be to add the chart (on a different form probably or extended/retract form).
    Attached Files Attached Files

  35. #35
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by Max187Boucher View Post
    I am not good with DB stuff, specially memDB so I'm not sure if I did it correctly but its working.
    I will update it eventually
    Maybe a few suggestions in that regard...

    Your mRequest.bas module is looking nice so far (codewise) - though from an architectural point of view,
    you should consider the case (e.g. when you plan to ship this as a shareware-App or something...)
    that possibly a few Users could have more than one account on BitFinex - so, encapsulating
    the Code you have in a Class instead of a Module, you would take that already "into account"...
    (just creating a new Class-Instance for each account, storing those potentially multiple Account-instances
    in a Project-global Collection-Class later on, or an in an Array: BFAccounts() As cBFAccount ...).

    So - a proper name for such a Class-Encapsulation would be e.g.: cBFAccount

    The Class contains the Functions of your module mRequest.bas - but I've also
    put the MemDB-Instance into this Class, to have "isolated" MemDB-instances
    for each Account.
    Code:
    Option Explicit
     
    Public Enum enumTicker
      Mid = 0
      Bid = 1
      Ask = 2
      LastPrice = 3
      low = 4
      high = 5
      Volume = 6
    End Enum
    
    Public BitFinex As cBitFinex, MemDB As cMemDB 'both Variables are reachable from outside, because defined Public
     
    Private Sub Class_Initialize()
      Set MemDB = New_c.MemDB 'create a new MemDB-Instance for each cBFAccount-ClassInstance
        
        'create an Orders Table which gets periodically synchronized from the Web
        'the definition below is basically the same as a MemDB.Exec "Create Table Orders(...)"-statement,
        'but it does this more "vertically" - easier to add new Fields - and more resembling a VB-TypeDef statement
        With MemDB.NewFieldDefs
          .Add "id              Text"
          .Add "side            Text"
          .Add "price           Text"
          .Add "original_amount Text"
          .Add "timestamp       Text"
        End With
        MemDB.CreateTable "Orders"
        
        'create a View (named vwOrders), which defines the formatted output of the Field-List of the Orders-Table
        'later on, you can use this ViewName as a shortcut -> as e.g. MemDB.GetRs("Select * From vwOrders")
        MemDB.Exec "Create View vwOrders As " & _
                   "Select id, side, price, Format$(original_amount,'0.00') As original_amount,  Format$(DateTime(timestamp, 'unixepoch','localtime'), 'mmm dd, yyyy  @  hh:mm AM/PM') As timestamp From Orders"
    End Sub
    
    Public Sub BitFinex_Init(API_KEY As String, API_SECRET_KEY As String)
      Set BitFinex = New cBitFinex
          BitFinex.Init API_KEY, API_SECRET_KEY
    End Sub
     
    Public Function Ticker(TickerPrice As enumTicker) As String
    Dim Result As cCollection
    Dim httpStatusText As String
    
      If BitFinex.DoUnauthenticatedRPC("/pubticker/btcusd", Result, httpStatusText) Then
          Ticker = Format(Result(Choose(TickerPrice + 1, "mid", "bid", "ask", "last_price", "low", "high", "volume")), "0.00")
      Else
          Ticker = httpStatusText
      End If
    End Function
     
    Public Function NewOrder(Price As String, Amount As String, BuyOrSell As String, Optional sType As String = "exchange limit", Optional sSymbol As String = "btcusd", Optional sExchange As String = "bitfinex", Optional bIsHidden As Boolean = False) As String
    Dim Args As cCollection
    Dim Result As cCollection
    Dim httpStatusText As String
      
      Set Args = New_c.JSONObject
          Args.Prop("symbol") = sSymbol
          Args.Prop("amount") = Amount
          Args.Prop("price") = Price
          Args.Prop("exchange") = sExchange
          Args.Prop("side") = LCase(BuyOrSell)
          Args.Prop("type") = LCase(sType)
          If bIsHidden Then Args.Prop("is_hidden") = bIsHidden
          
      If BitFinex.DoAuthenticatedRPC("/order/new", Result, httpStatusText, Args) Then
        NewOrder = Result("order_id")
      Else
        NewOrder = httpStatusText
      End If
    End Function
     
    Public Function UpdateOrders() As String
    Dim Result As cCollection
    Dim R As cCollection 'define the Type of the *Item* the Result-Collection contains (in this case it is other cCollection-instances)
    Dim httpStatusText As String
     
      If BitFinex.DoAuthenticatedRPC("/orders", Result, httpStatusText) Then
          MemDB.Exec "Delete From Orders" 'cleanup all existing records from the Orders-Table (since we got more current, new content in the Result-Collection)
     
          For Each R In Result 'we can iterate over the Items in the Result-Collection also per For Each
            'MemDB.ExecCmd is a convenience-method to easier handle CommandObjects (first param gets the SQL, followed by the Values for the '?' slots)
            MemDB.ExecCmd "Insert Into Orders Values(?,?,?,?,?)", R("id"), R("side"), R("price"), R("original_amount"), R("timestamp")
          Next
          
          UpdateOrders = "Successful"
      Else
          UpdateOrders = httpStatusText
      End If
    End Function
    
    Public Function GetOrderInfo(sOrderID As String) As String
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim OrderID As cCollection
     
        Set OrderID = New_c.JSONObject
            OrderID.Prop("order_id") = Val(sOrderID)
        
        If BitFinex.DoAuthenticatedRPC("/order/status", Result, httpStatusText, OrderID) Then
          Debug.Print Result.Item("is_live")
          Debug.Print Result.Item("price")
          Debug.Print Result.Item("original_amount")
        Else
          GetOrderInfo = httpStatusText
        End If
    End Function
     
    Public Function CancelOrder(Optional sOrderID As String, Optional CancelAll As Boolean = False)
    Dim Result As cCollection
    Dim httpStatusText As String
    Dim OrderID As cCollection
    
        If CancelAll Then
            BitFinex.DoAuthenticatedRPC "/order/cancel/all", Result, httpStatusText, OrderID
        Else
            Set OrderID = New_c.JSONObject
                OrderID.Prop("order_id") = Val(sOrderID)
            BitFinex.DoAuthenticatedRPC "/order/cancel", Result, httpStatusText, OrderID
        End If
        CancelOrder = httpStatusText
    End Function
    A few more comments to the MemDB-instance, which is contained in this Class:

    Its lifetime is now bound to the lifetime of its hosting cBFAccount-Class -
    so - in case we talk about a single BitFinex-Account - and you create it
    for example in a modMain.bas-Module variable as:
    Code:
    Public MyAccount As cBFAccount
    
    Sub Main()
      Set MyAccount = New cBFAccount
          MyAccount.BitFinex_Init My_Key, My_Secret_Key
    End Sub
    The MemDB-instance is created (along with its Tables) in Class_Initialize
    of its hosting Class - and will live as long as the MyAccount-ClassInstance lives
    (usually as long as the App lives, unless you set the MyAccount-Variable
    to Nothing a bit earlier than on App-Shutdown).

    Anytime you need to request Data from this "Account-specific MemDB", you can do
    (e.g. from within a Form, which contains a MSHFlexGrid)...

    Code:
    Private Sub UpdateOrdersGrid(HFlex As MSHFlexGrid)
      Set HFlex.DataSource = MyAccount.MemDB.GetRs("Select * From vwOrders Order By timestamp Desc").DataSource
    End Sub
    to show a listing, ordered by descending timestamps (newest first)...

    or alternatively use a listing which is ordered by id in ascending order (as shown below).
    Code:
    Private Sub UpdateOrdersGrid(HFlex As MSHFlexGrid)
      Set HFlex.DataSource = MyAccount.MemDB.GetRs("Select * From vwOrders Order By id Asc").DataSource
    End Sub
    I think these short SQL-snippets show, why I removed the passing of "concrete GUI-Controls"
    from your methods in mRequest.bas (and introduced a View-Definition in Class_Initialize of cBFAccount).

    It's better to make the code in such "Data-Classes" independent from any Control-Types, to keep
    them flexible to use with *any* kind of GUI-ListControls.

    For a "multicolumn-view", you could use also the VB6-DataGrid- or a ListView-Control as a visualizing container -
    but also a normal VB6-ListBox, as in the example below, to e.g. show only the Order-timestamps in the listbox,
    and using Order-id as the ListBox-ItemData (to retrieve details later in a ListBox-Click-Event):
    Code:
    Private Sub UpdateOrdersLB(LB As ListBox)
      LB.Clear
      'restrict the returned Rs to contain only the Fields: id and timestamp
      With MyAccount.MemDB.GetRs("Select id, timestamp From vwOrders Order By timestamp Desc")
        Do Until .EOF
          LB.AddItem !timestamp
          LB.ItemData(LB.NewIndex) = !id
          .MoveNext
        Loop
      End With
    End Sub
    So the MemDB is acting as a "universal Data-Container" which can host more than one Table
    (e.g. your Ticker-Data could be stored in a Ticker-Table as well) - and since MemDB is
    already "exposed Publically" (as a Property of your Account-ClassInstance), you can
    retrieve Data (Recordsets) from its Data-Tables any time you choose to in your GUI-code,
    ordered, filtered, restricted to a few fields of the table - definable with great flexibility
    over the SQL-Select-string.

    All you have to do is, to keep the Data in MemDB "up-to-date" (in proper intervals).

    E.g. using a Timer on your Main-Form you would simply have to do:
    Code:
    Private Sub tmrUpdateLocalAccountData_Timer()
      MyAccount.UpdateOrders
      'MyAccount.UpdateTickerData
    End Sub
    The above Timer-based Data-Refreshing can act entirely independent from your GUIs ListControl-Refreshes
    (they just ensure, that anytime you "reach into the MemDB", the Data is "quite recent").

    Olaf
    Last edited by Schmidt; Apr 25th, 2015 at 10:34 AM.

  36. #36
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    I'm not sure how the (bizarre to my eye) convention of naming a VB6 Class with a "c" prefix ever came from.

    Normally a "hungarian prefix" is meant to indicate the data type and not... module type? So cBFAccount seems like a really odd choice. Why not BFAccount instead?

    After all we have Collection, TextBox, Command, etc. rather than cCollection, cTextBox, cCommand, etc. So why on Earth would one create new classes named that way? I just don't see the benefit and it seems to jar the eye and not fit with normal VB naming conventions at all.

    What am I missing? There must be some advantage I'm unaware of.

  37. #37
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by dilettante View Post
    I'm not sure how the (bizarre to my eye) convention of naming a VB6 Class with a "c" prefix ever came from.
    Normally a "hungarian prefix" is meant to indicate the data type and not... module type?
    A ClassName *is* a user-definable DataType (as are Enums, or "normal TypeDefs" - and as are
    Form-Classes, which I prefix with 'f' ...and also UserControl-Classes, which I prefix with 'uc' for example).

    Other Class-Prefixes I use are e.g.:
    - 'cw' for WidgetClasses (similar to the 'uc' prefixing in case of normal VB-UserControls)
    - 'cf' for cWidgetForm-Classes (similar to the 'f' prefixing in case of normal VB-Forms)

    I've explained the (for me obvious) advantages already in a reply to you here (in posting #10):
    http://www.vbforums.com/showthread.p...=1#post4473729

    But after all it's a matter of personal taste (easily changeable to your own naming-scheme) -
    important perhaps only, what Bonnie West mentioned in the above linked thread:
    "Be consistent about it in your own CodeBase".

    Olaf

  38. #38

    Thread Starter
    PowerPoster
    Join Date
    Aug 2011
    Location
    B.C., Canada
    Posts
    2,887

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    I have changed to your suggestions which look a lot nicer and clearer (coding wise).
    I had a small issue tho (not on your part) but it "lags" when sending the request through http... would it be possible to make the cBitFinex class call the http request on a different thread so my app does not "freeze until request is processed" I thought to put a timeout but it seems like it still lags but not as long and sometime does not have enough time to get the information before it times out.... I know you RC5 has multithread well how would I use it for this?

    Note: My computer is a bit far from my wireless modem (thanks to the great ISP service that didn't want to put it where I wanted the modem "in the middle of the house, and not in the furthest corner")

    Maybe that is why it lags a bit, but still multithread simply for the bitfinex request would save my GUI from not responding.

    Form Code:
    Code:
    Private BFAccount() As cBFAccount
    
    
    
    Private Sub cmdAddAccount_Click()
      Add_BFAccount sKey, sKeySecret
    End Sub
    
    Private Sub cmdBuy_Click()
      BFAccount(0).NewOrder txtPrice.Text, txtAmount.Text, "buy", cmbOrder.Text
    End Sub
    
    
    Private Sub cmdSell_Click()
      BFAccount(0).NewOrder txtPrice.Text, txtAmount.Text, "sell", cmbOrder.Text
    End Sub
    
    
    Private Sub Command1_Click()
    'Cancel Single Order
      BFAccount(0).CancelOrder Left(lstOrders.List(lstOrders.ListIndex), 9)
    'Cancel All Orders
      'BFAccount(0).CancelOrder , True
    End Sub
    
    
    Private Sub lstOrders_Click()
      BFAccount(0).GetOrderInfo Left(lstOrders.List(lstOrders.ListIndex), 9)
    End Sub
    
    
    Private Sub Add_BFAccount(bfKey As String, bfKeySecret As String)
        
        If UBound(BFAccount) = 0 Then
                  Set BFAccount(0) = New cBFAccount
                      BFAccount(0).BitFinex_Init bfKey, bfKeySecret
        Else
            ReDim Preserve BFAccount(UBound(BFAccount) + 1)
                       Set BFAccount(UBound(BFAccount)) = New cBFAccount
                           BFAccount(UBound(BFAccount)).BitFinex_Init bfKey, bfKeySecret
        End If
    End Sub
    
    
    Private Sub UpdateOrdersGrid(HFlex As MSHFlexGrid)
      Set HFlex.DataSource = BFAccount(0).MemDB.GetRs("Select * From vwOrders Order By timestamp Desc").DataSource
    End Sub
    
    
    Private Sub Form_Load()
      ReDim BFAccount(0)
    End Sub
    
    
    Private Sub Timer1_Timer()
    Static LastMid As String
    Dim sTicker As String
    
      sTicker = BFAccount(0).Ticker(Mid)
      
      If LastMid <> sTicker Then
        LastMid = sTicker
        lstTrend.AddItem sTicker
      End If
      
      lblLow.Caption = BFAccount(0).Ticker(low)
      lblHigh.Caption = BFAccount(0).Ticker(high)
      
      UpdateOrdersGrid flxOrderList
    End Sub
    And also made the two modules into a class cBFAccount like you suggested
    I don't know if BitFinex would allow two account on one IP tho... proxy would work though but we will leave this for later if required... we'll just make it so it is available but not coded yet (for later use so we don't have to change the whole classes and code)


    Edit:
    I can't seem to find on the API page where it shows how to get the Bids/Asks list like on the webpage, I can get them webscraping but I'm using the API there must be something I'm missing, its not book/lendbook do you know which one it could be?
    Last edited by Max187Boucher; Apr 26th, 2015 at 09:06 PM.

  39. #39
    Fanatic Member namrekka's Avatar
    Join Date
    Feb 2005
    Location
    Netherlands
    Posts
    639

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Quote Originally Posted by Max187Boucher View Post
    Edit:
    I can't seem to find on the API page where it shows how to get the Bids/Asks list like on the webpage, I can get them webscraping but I'm using the API there must be something I'm missing, its not book/lendbook do you know which one it could be?
    Look at the "Orderbook".

  40. #40
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,255

    Re: [RESOLVED] Help communicating with web API (bitfinex)

    Yep, Orderbook is quite similar to the Lendbook-query, which we already have an example
    (further above somewhere)...

    Here' Lendbook in conjunction with the MemDB-storage:

    Code:
    Dim BitFinex As New cBitFinex, Result As cCollection, httpStatusText As String, Args As cCollection
    
    If BitFinex.DoUnauthenticatedRPC("/lendbook/btc", Result, httpStatusText) Then
    
      Dim R As cCollection
      
      MemDB.Exec "Delete From LendbookBids"
      For Each R In Result("bids")
        MemDB.ExecCmd "Insert Into LendbookBids Values(?,?,?,?,?)", R("rate"), R("amount"), R("period"), R("timestamp"), R("frr")
      Next
      
      MemDB.Exec "Delete From LendbookAsks"
      For Each R In Result("asks")
        MemDB.ExecCmd "Insert Into LendbookAsks Values(?,?,?,?,?)", R("rate"), R("amount"), R("period"), R("timestamp"), R("frr")
      Next
    
    Else
      Debug.Print httpStatusText
    End If
    The Orderbook-query comes with only 3 instead 5 members in "bids" and "asks" - but the loops should be done in the same way
    (e.g. using OrderBids and OrderAsks as the table-names).

    Olaf

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width