2016-10-29 71 views
0

背景
ETrade認證系統讓我創建一個RequestToken,然後執行一個授權URL,它打開一個ETrade頁面。 用戶登錄以授權其帳戶上的活動。 他們收到一個針,他們進入我的應用程序。 我使用RequestToken和Pin調用ExchangeRequestTokenForAccessToken。 然後我們關閉並運行。ETrade API無人值守認證

問題
問題是我正在創建一個在後臺連續運行的服務。將不會有任何用戶登錄。相反,我不會進行任何交易。只需琢磨一下數字,尋找符合特定標準的股票。 我不知道如何讓這個工作無人看管。

謝謝布拉德。

+0

如果未設置其他值,令牌將在午夜過期。 但是,您可以調用etrade並請求持久性令牌,然後在30天后過期。不建議用於交易等。認證過程中不需要更改代碼。只有在他們即將到期並提醒他們續約時纔會預測。 – BWhite

回答

1

此前,我使用了一系列WebRequests並手動添加標題來模擬授權頁面。直到大約一年前,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); 
      } 
+0

這是一個很大的幫助。由於WatiN和ETrade已經改變,我已經發布了下面的更新版本。 – BWhite

1

的布拉德·梅爾頓的代碼當前版本。 WatiN已更改,不再包含IE.AttachToIE函數。 因此,IEStaticInstanceHelper現在被稱爲StaticBrowserInstanceHelper,但該代碼很難找到,所以我將它包含在這裏。

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的登錄頁面也發生了變化。他們有幾個。我一直檢查的所有登錄頁面都有一個USER字段和一個PASSWORD字段,但登錄按鈕有不同的名稱,看起來很脆弱。所以如果這不起作用,那我首先會檢查。第二,如果我直接進入驗證頁面,它會提示登錄,但是它經常不會將您帶到驗證頁面。 通過進入主頁登錄,然後進入auth頁面,我獲得了更一致的結果。

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; 
     } 
    } 

能夠自動化它,這意味着我不再關心令牌有效的時間。感謝BradM!