2

バックグラウンド
ETrade 認証システムでは、RequestToken を作成し、認証 URL を実行する必要があります。これにより ETrade ページが開きます。ユーザーはログインして、自分のアカウントでのアクティビティを承認します。彼らはピンを受け取り、それを私のアプリに入力します。RequestToken と Pin を指定して ExchangeRequestTokenForAccessToken を呼び出します。それから私たちはオフにして走っています。

質問
問題は、バックグラウンドで継続的に実行されるサービスを作成していることです。ログインするユーザーはいません。逆に、私は取引を行いません。数値をざっと見て、特定の基準を満たす銘柄を探します。これを無人で機能させる方法がわかりません。

ありがとう、ブラッド。

4

2 に答える 2

1

以前は、一連の WebRequest を使用し、ヘッダーを手動で追加して認証ページをシミュレートしました。これは、約 1 年前に ETrade が情報を追跡しているように見える何かでヘッダーを複雑にするまで機能していました。私は今、http://watin.org/を使ってログインし、認証コードを取り除いています。

ずさんなコードは次のようになります。

            using WatiN.Core; // IE Automation
...
            // verify current thread in STA.

            Settings.Instance.MakeNewIeInstanceVisible = false;

            var ieStaticInstanceHelper = new IEStaticInstanceHelper();
            Settings.AutoStartDialogWatcher = false;

            using (ieStaticInstanceHelper.IE = new IE())
            {
                string authCode = "";
                ieStaticInstanceHelper.IE.GoTo(GetAuthorizationLink());

                if (ieStaticInstanceHelper.IE.ContainsText("Scheduled System Maintenance"))
                {
                    throw new ApplicationException("eTrade down for maintenance.");
                }

                TextField user = ieStaticInstanceHelper.IE.TextField(Find.ByName("USER"));

                TextField pass = ieStaticInstanceHelper.IE.TextField(Find.ById("txtPassword"));

                TextField pass2 = ieStaticInstanceHelper.IE.TextField(Find.ByName("PASSWORD"));

                Button btn = ieStaticInstanceHelper.IE.Button(Find.ByClass("log-on-btn"));
                Button btnAccept = ieStaticInstanceHelper.IE.Button(Find.ByValue("Accept"));


                TextField authCodeBox = ieStaticInstanceHelper.IE.TextField(Find.First());

                if (user != null && pass != null && btn != null &&
                    user.Exists && pass2.Exists && btn.Exists)
                {
                    user.Value = username;
                    pass2.Value = password;
                    btn.Click();
                }

                btnAccept.WaitUntilExists(30);
                btnAccept.Click();

                authCodeBox.WaitUntilExists(30);
                authCode = authCodeBox.Value;

                SavePin(authCode);
            }
于 2016-12-06T21:26:43.213 に答える
1

Current version of Brad Melton's code. WatiN has changed and no longer contains the IE.AttachToIE function. So, IEStaticInstanceHelper is now called StaticBrowserInstanceHelper, but that code is hard to find, so I've included it here.

class StaticBrowserInstanceHelper<T> where T : Browser {
    private Browser _browser;
    private int _browserThread;
    private string _browserHwnd;

    public Browser Browser {
        get {
            int currentThreadId = GetCurrentThreadId();
            if (currentThreadId != _browserThread) {
                _browser = Browser.AttachTo<T>(Find.By("hwnd", _browserHwnd));
                _browserThread = currentThreadId;
            }
            return _browser;
        }
        set {
            _browser = value;
            _browserHwnd = _browser.hWnd.ToString();
            _browserThread = GetCurrentThreadId();
        }
    }

    private int GetCurrentThreadId() {
        return Thread.CurrentThread.GetHashCode();
    }
}

ETrade's login pages have changed as well. They have several. All the login pages I checked consistently had a USER field and a PASSWORD field, but the login buttons had various names that look fragile. So if this doesn't work, that's the first thing I'd check. Second, if I go directly to the auth page, it prompts to log in, but then it frequently doesn't take you to the auth page. I got more consistent results by going to the home page to log in, then going to the auth page.

    static public string GetPin(string username, string password, string logonLink, string authLink) {
        // Settings.Instance.MakeNewIeInstanceVisible = false;

        var StaticInstanceHelper = new StaticBrowserInstanceHelper<IE>();
        Settings.AutoStartDialogWatcher = false;

        // This code doesn't always handle it well when IE is already running, but it won't be in my case. You may need to attach to existing, depending on your context. 
        using (StaticInstanceHelper.Browser = new IE(logonLink)) {
            string authCode = "";

            // Browser reference was failing because IE hadn't started up yet.
            // I'm in the background, so I don't care how long it takes.
            // You may want to do a WaitFor to make it snappier.
            Thread.Sleep(5000);
            if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
                throw new ApplicationException("eTrade down for maintenance.");
            }

            TextField user = StaticInstanceHelper.Browser.TextField(Find.ByName("USER"));

            TextField pass2 = StaticInstanceHelper.Browser.TextField(Find.ByName("PASSWORD"));

            // Class names of the Logon and Logoff buttons vary by page, so I find by text. Seems likely to be more stable.
            Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ByText("Log On"));
            Element btnLogOff = StaticInstanceHelper.Browser.Element(Find.ByText("Log Off"));
            Button btnAccept = StaticInstanceHelper.Browser.Button(Find.ByValue("Accept"));

            TextField authCodeBox = StaticInstanceHelper.Browser.TextField(Find.First());

            if (user != null && btnLogOn != null &&
                user.Exists && pass2.Exists && btnLogOn.Exists) {
                user.Value = username;
                pass2.Value = password;
                btnLogOn.Click();
            }

            Thread.Sleep(1000);
            if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
                Element btnContinue = StaticInstanceHelper.Browser.Element(Find.ByName("continueButton"));
                if (btnContinue.Exists)
                    btnContinue.Click();
            }
            btnLogOff.WaitUntilExists(30);

            // Here we go, finally.
            StaticInstanceHelper.Browser.GoTo(authLink);
            btnAccept.WaitUntilExists(30);
            btnAccept.Click();

            authCodeBox.WaitUntilExists(30);
            authCode = authCodeBox.Value;
            StaticInstanceHelper.Browser.Close();

            return authCode;
        }
    }

Being able to automate it like this means that I no longer care about how long the token is valid. Thanks BradM!

于 2017-01-08T06:30:23.817 に答える