15

I woud like to buy on gdax automatically. But my inputs in the Amount window doesn´t get recognized. I can see that on the little field, that says: Total (LTC) ≈ 0.00000000

My code:

Sub test()

    Dim ObjIE As New InternetExplorer
    Dim Ohtml As HTMLDocument
    Dim HTMLtags As IHTMLElementCollection
    Dim HTMLtag As IHTMLElement
    Dim HTMLobjekt As IHTMLElement
    Dim item_limit As Object
    Dim y As Integer

    With ObjIE
        .Visible = True
        .navigate "https://www.gdax.com/trade/LTC-EUR"
        Do Until .readyState = READYSTATE_COMPLETE: Loop
        Set Ohtml = .document
    End With

    'Amount
    Do
        Set HTMLtags = Ohtml.getElementsByClassName("OrderForm_input-box_XkGmi")
        DoEvents
    Loop While HTMLtags.Length = 0
    For Each HTMLtag In HTMLtags
        If HTMLtag.Children(1).innerText = "EUR" Then
            Set HTMLobjekt = HTMLtag.Children(0)
            HTMLobjekt.Value = 100      ' this is the part that i excanged
        End If
    Next HTMLtag

    'get the Total(LTC) to cross check
    Do
        Set HTMLtags = Ohtml.getElementsByClassName("OrderForm_total_6EL8d")
        DoEvents
    Loop While HTMLtags.Length = 0
    For Each HTMLtag In HTMLtags
        Debug.Print HTMLtag.innerText & "Total(LTC)"
    Next HTMLtag

End Sub

This is what the website says when the code is done:

Picture.this is how it is

and this is how it should look like, and looks when I type the number in manually:

Picture.this is how it should be

I also exchange the marked part with things like:

HTMLobjekt.innerText = 100

or

HTMLobjekt.innerText = "100"

or

HTMLobjekt.Value = "100"

but nothing worked.

S Meaden
  • 8,050
  • 3
  • 34
  • 65
Nils
  • 275
  • 3
  • 12
  • 1
    From browser developer tools you can see, that the amount is updated by `input` type event bubbled on the `document` level, so you need to create that event object and dispatch it to the target `HTMLobjekt` node. Note `` node and `input` type event is completely different things just having the same name. – omegastripes Feb 04 '18 at 22:32
  • 1
    can you give an example of how you mean that? I still can´t find it out by my self. I would also be interested, how you found that. If you have a good website were i can read that, just send it. – Nils Feb 07 '18 at 21:41
  • 1
    Use the api: https://docs.gdax.com/ – Florent B. Feb 09 '18 at 14:00
  • 1
    Nils, are you sure you need _Total (LTC)_ to be updated to automate buying? Seems that is for visualization only. I checked XHR logged after I clicked _Place_ button, and the only value is sent in parameters is the entered amount. See steps 1-5 on the [screenshot](https://i.stack.imgur.com/H8tdt.png). Most efficient solution is API usage, as @FlorentB pointed. – omegastripes Feb 10 '18 at 19:18

3 Answers3

10

You need to make sure the page is fully loaded before you take any initiative. I checked for the availability of one such class generated dynamically OrderBookPanel_text_33dbp. Then I did the rest what you tried to do. Lastly, you need to Focus the target before putting that amount in the placeholder. Applying all of the above, your script should more like below:

Sub Get_Value()
    Dim HTML As HTMLDocument, tags As Object
    Dim tag As Object, ival As Object

    With CreateObject("InternetExplorer.Application")
        .Visible = True
        .navigate "https://www.gdax.com/trade/LTC-EUR"
        While .Busy = True Or .readyState < 4: DoEvents: Wend
        Set HTML = .document

        Do: Set tags = HTML.querySelector("[class^='OrderBookPanel_text_']"): DoEvents: Loop While tags Is Nothing
        Do: Set tag = HTML.querySelector("input[name='amount']"): DoEvents: Loop While tag Is Nothing
        tag.Focus
        tag.innerText = 100

        Set ival = HTML.querySelector("[class^='OrderForm_total_']")
        [A1] = ival.innerText
        .Quit
    End With
End Sub

Output at this moment:

0.79139546
SIM
  • 21,997
  • 5
  • 37
  • 109
5

Not a complete answer, just a direction. Open the webpage https://www.gdax.com/trade/LTC-EUR in a browser (e. g. Chrome). Click to open context menu on the target element (step 1 on the below screenshot), click Inspect (2), from opened developer tools on the right you can see target element (3), and that there is a bunch of event listeners attached to target object (4). One of the handlers is input on the document node level. Actually the amount is updated by this event handler, when event is bubbled from <input> node. You can easily check that by deleting all other event handlers (click on their Remove buttons). But if you delete particularly this input handler (5) then there will be no update (until you reload the webpage).

F12

So, to mimic user activity you need to create such input event object and dispatch it to the target <input> node. Note <input> node and input control event is completely different things just having the same name.

Tracing event handler execution in IE can be done in debugger. Open IE developer tools (press F12), go to Debugger tab (step 1 on the below screenshot), Breakpoints tab (2), click Add event breakpoint (3), choose input event (4). Now any input event will pause code execution and open debugger window (5).

debugger

If you try to type in the amount textbox on the webpage, debugger will show event handler function code:

handler

You may resume execution (F5), or make step into / over / out. To see event object passed to the function, type arguments in console, there is output for standard event handler call after typing manually:

called by typing

I tested the below test code to trigger that event from VBA:

Sub Test()

    Dim oEvt As Object

    With CreateObject("InternetExplorer.Application")
        .Visible = True
        .Navigate "https://www.gdax.com/trade/LTC-EUR"
        Do While .ReadyState < 4 Or .Busy
            DoEvents
        Loop
        With .Document
            Do While IsNull(.querySelector("div.OrderForm_input-box_XkGmi input"))
                DoEvents
            Loop
            Set oEvt = .createEvent("Event")
            oEvt.initEvent "input", True, False, .parentWindow, 1
            With .querySelector("div.OrderForm_input-box_XkGmi input")
                .Focus
                .Value = "1000"
                .dispatchEvent oEvt
                Stop
            End With
        End With
    End With

End Sub

As a result, absolutely the same actions take place as in case of typing the amount manually. It works without any errors. But Total (LTC) isn't updated. Debugger pauses execution as well, and the output for event object is as follows:

called by VBA

The only difference is that isTrusted property is True for standard call, and False for call via VBA. I guess that is why updating is skipped somewhere in the handler code. I tried to trace the entire code execution till event handler completion, but seems that is huge reverse engineering work, which I can't devote time for at the moment.

Currently I'm stucked in my investigations at that point.

omegastripes
  • 12,351
  • 4
  • 45
  • 96
  • thanks for that. Do you have any further ideas of how i chould trigger this input Event. the only things that i could find was something like this `HTMLobjekt.FireEvent ("oninput")` or, becaus you said it is on the document level, `Ohtml.FireEvent ("oninput")` but none of this worked – Nils Feb 08 '18 at 20:29
  • @Nils I have tried to trigger event via VBA, but to no avail, so far. Check answer update. – omegastripes Feb 10 '18 at 18:49
0

Sorry could not get to work but have nudged along the problem.

I managed to figure out the correct syntax for creating and raising an event in higher versions of Internet Explorer (thanks to this SO answer ) . I managed to raise an input event for the input box, that followed on from omegastripes excellent lead.

So it works for a local file which I give below that has the same essential structure of the real page.

But when I run the code on the real page it simply does not work and I do not know why.

Where possible I'm feeding javascript into IE for it to execute to miminise issues with the VBA/COM/MSHTML interoperability bridge. I do this by calling window.execScript passing the javascript as a string.

I hope someone can pick up this ball and run it over the goal line. For me, I have reached the end.

Here is the local html file

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <input id="Button1" type="button" value="Programmatically write textbox value" onclick="TestAlert()" />

    <form class="OrderForm_form_25r0u">
        <ul class="OrderForm_trade-type_2QyK4">
            <li class="OrderForm_trade-type-tab_uWGMp OrderForm_active_Di-9p">MARKET</li>
            <li class="OrderForm_trade-type-tab_uWGMp">LIMIT</li>
            <li class="OrderForm_trade-type-tab_uWGMp">STOP</li>
        </ul>
        <ul class="OrderForm_toggle_120Ka">
            <li class="OrderForm_toggle-tab_bZZnC OrderForm_buy_38n5g OrderForm_active_Di-9p">BUY</li>
            <li class="OrderForm_toggle-tab_bZZnC OrderForm_sell_3vYRQ">SELL</li>
        </ul>
        <div class="market-order">
            <div class="OrderForm_section_2Znad">
                <div class="OrderForm_section-header_fwFDB">Amount</div>
                <div class="OrderForm_input-box_XkGmi">
                    <input type="number" step="0.01" min="0" name="amount" placeholder="0.00" value="" autocomplete="off" oninput="myOnInputHandler()">
                    <span>EUR</span>
                </div>
            </div>
        </div>
        <div class="OrderForm_order-total_3Mkdz">
            <div>
                <b>Total</b>
                <span>(LTC)</span>
                <b>≈</b>
            </div>
            <div class="OrderForm_total_6EL8d" >0.00000000</div>
        </div>
    </form>

    <script language="javascript">
        function myOnInputHandler() {
            print_call_stack();
            alert('you input something');
        }

        function print_call_stack() { console.trace(); }

        function print_call_stack2() {
            var stack = new Error().stack;
            console.log("PRINTING CALL STACK");
            console.log(stack);
        }


        function TestAlert() { setInputBox(document); }

        function setInputBox() {
            try {
                var inputBox = document.querySelector('div.OrderForm_input-box_XkGmi input'); inputBox.value = 100; if (document.createEvent) { var event2 = document.createEvent("HTMLEvents"); event2.initEvent("input", true, true); event2.eventName = "input"; inputBox.dispatchEvent(event2); }

                return ({ success: true });
            }
            catch (ex) {
                return ({ exception: ex, myMsg: '#error in setInputBox!' });
            }
        }

    </script>
</body>
</html>

Here is the VBA

Option Explicit

'* Tools - References
'*      MSHTML      Microsoft HTML Object Library                   C:\Windows\SysWOW64\mshtml.tlb
'*      SHDocVw     Microsoft Internet Controls                     C:\Windows\SysWOW64\ieframe.dll
'*      Shell32     Microsoft Shell Controls And Automation         C:\Windows\SysWOW64\shell32.dll


Private Function ReacquireInternetExplorer(ByVal sMatch As String) As Object
    Dim oShell As Shell32.Shell: Set oShell = New Shell32.Shell
    Dim wins As Object: Set wins = oShell.Windows
    Dim winLoop As Variant
    For Each winLoop In oShell.Windows
        If "C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE" = winLoop.FullName Then

            Dim sFile2 As String
            sFile2 = "file:///" & VBA.Replace(sMatch, "\", "/")
            If StrComp(sFile2, winLoop.LocationURL, vbTextCompare) = 0 Then
                Set ReacquireInternetExplorer = winLoop.Application
                GoTo SingleExit
            End If
        End If
    Next
SingleExit:
End Function

Sub test()

    Dim objIE As InternetExplorer
    Set objIE = New InternetExplorer
    Dim oHtml As HTMLDocument
    Dim HTMLtags As IHTMLElementCollection


    Dim sUrl As String
    sUrl = "C:\Users\Simon\source\repos\WebApplication2\WebApplication2\HtmlPage1.html"
    'sUrl = "http://localhost:50367/HtmlPage1.html"
    sUrl = "https://www.gdax.com/trade/LTC-EUR"

    objIE.Visible = True
    objIE.Navigate sUrl

    If StrComp(Left(sUrl, 3), "C:\") = 0 Then
        Stop '* give chance to clear the activex warning box for the local file
        Set objIE = ReacquireInternetExplorer(sUrl)
    End If
    Do Until objIE.readyState = READYSTATE_COMPLETE: DoEvents: Loop
    Set oHtml = objIE.Document

    Do
        '* wait for the input box to be ready
        Set HTMLtags = oHtml.getElementsByClassName("OrderForm_input-box_XkGmi")
        DoEvents
    Loop While HTMLtags.Length = 0

    Dim objWindow As MSHTML.HTMLWindow2
    Set objWindow = objIE.Document.parentWindow


    '* next line would be really useful if it worked because it prints the stack trace in the console window
    '* and we would be able to see the needle in the haystack where te order total gets updated
    '* works on local file but not on https://www.gdax.com/trade/LTC-EUR  **sigh**
    objWindow.execScript "var divTotal = document.querySelector('div.OrderForm_total_6EL8d'); divTotal.onchange = function() { console.trace(); }"


    '* next line sets the input box and raises an event, works on local file but not on GDAX
    objWindow.execScript "var inputBox = document.querySelector('div.OrderForm_input-box_XkGmi input'); inputBox.value = 100; if (document.createEvent) { var event2 = document.createEvent('HTMLEvents'); event2.initEvent('input', false, false); event2.eventName = 'input'; inputBox.dispatchEvent(event2);  }"


    'get the Total(LTC) to cross check
    Do
        '* wait for the order total div to be ready
        Set HTMLtags = oHtml.getElementsByClassName("OrderForm_total_6EL8d")
        DoEvents
    Loop While HTMLtags.Length = 0

    Dim divTotal As HTMLDivElement
    Set divTotal = oHtml.querySelector("div.OrderForm_total_6EL8d")
    Debug.Print divTotal.innerText & " Total(LTC)"

    Stop

End Sub
S Meaden
  • 8,050
  • 3
  • 34
  • 65
  • Seems `isTrusted` property of event object set to `False` from VBA prevents _Total (LTC)_ updating. Check my answer update. – omegastripes Feb 10 '18 at 18:51
  • @omegastripes : I'll have another go based on your updates, glad to see you got the DOM breakpoints working. Did you see I tried to get a console trace for when the calculation gets updated? I agree is huge reverse engineering task. – S Meaden Feb 10 '18 at 19:02
  • Yes, I've tried `console.trace();` feature from your answer too. Generally IE call stack tab may also help while debug. – omegastripes Feb 10 '18 at 19:09