@@ -248,6 +248,7 @@ public sealed class InteractiveBrokersBrokerage : Brokerage, IDataQueueHandler,
248248
249249 private volatile bool _isDisposeCalled ;
250250 private bool _isInitialized ;
251+ private bool _pastFirstConnection ;
251252
252253 private bool _historyHighResolutionRateLimitWarning ;
253254 private bool _historySecondResolutionWarning ;
@@ -829,6 +830,22 @@ public override void Connect()
829830 return ;
830831 }
831832
833+ Log . Trace ( "InteractiveBrokersBrokerage.Connect(): not connected, start connecting now..." ) ;
834+
835+ var lastAutomaterStartResult = _ibAutomater . GetLastStartResult ( ) ;
836+ if ( lastAutomaterStartResult . HasError )
837+ {
838+ lastAutomaterStartResult = _ibAutomater . Start ( false ) ;
839+ CheckIbAutomaterError ( lastAutomaterStartResult ) ;
840+ // There was an error but we did not throw, must be another 2FA timeout, we can't continue
841+ if ( lastAutomaterStartResult . HasError )
842+ {
843+ // we couldn't start IBAutomater, so we cannot connect
844+ OnMessage ( new BrokerageMessageEvent ( BrokerageMessageType . Warning , "IBAutomaterWarning" , $ "Unable to restart IBAutomater: { lastAutomaterStartResult . ErrorMessage } ") ) ;
845+ return ;
846+ }
847+ }
848+
832849 _stateManager . IsConnecting = true ;
833850
834851 var attempt = 1 ;
@@ -1001,6 +1018,7 @@ public override void Connect()
10011018 // if we reached here we should be connected, check just in case
10021019 if ( IsConnected )
10031020 {
1021+ _pastFirstConnection = true ;
10041022 Log . Trace ( "InteractiveBrokersBrokerage.Connect(): Restoring data subscriptions..." ) ;
10051023 RestoreDataSubscriptions ( ) ;
10061024
@@ -5038,6 +5056,16 @@ private void OnIbAutomaterOutputDataReceived(object sender, OutputDataReceivedEv
50385056 var resultHandler = Composer . Instance . GetPart < IResultHandler > ( ) ;
50395057 resultHandler ? . DebugMessage ( "Logging into account. Check phone for two-factor authentication verification..." ) ;
50405058 }
5059+ else if ( e . Data . Contains ( "2FA maximum attempts reached" , StringComparison . InvariantCultureIgnoreCase ) )
5060+ {
5061+ Task . Factory . StartNew ( ( ) =>
5062+ {
5063+ _ibAutomater . Stop ( ) ;
5064+ var message = "2FA authentication confirmation required to reconnect." ;
5065+ OnMessage ( BrokerageMessageEvent . Disconnected ( message ) ) ;
5066+ OnMessage ( new BrokerageMessageEvent ( BrokerageMessageType . ActionRequired , "2FAAuthRequired" , message ) ) ;
5067+ } ) ;
5068+ }
50415069
50425070 Log . Trace ( $ "InteractiveBrokersBrokerage.OnIbAutomaterOutputDataReceived(): { e . Data } ") ;
50435071 }
@@ -5220,6 +5248,10 @@ private void OnIbAutomaterExited(object sender, ExitedEventArgs e)
52205248
52215249 // check if IBGateway was closed because of an IBAutomater error, die if so
52225250 var result = _ibAutomater . GetLastStartResult ( ) ;
5251+ if ( IsRecuperable2FATimeout ( result ) )
5252+ {
5253+ return ;
5254+ }
52235255 CheckIbAutomaterError ( result , false ) ;
52245256
52255257 if ( ! result . HasError )
@@ -5247,9 +5279,14 @@ private void OnIbAutomaterExited(object sender, ExitedEventArgs e)
52475279 {
52485280 Log . Trace ( "InteractiveBrokersBrokerage.OnIbAutomaterExited(): restarting..." ) ;
52495281
5250- CheckIbAutomaterError ( _ibAutomater . Start ( false ) ) ;
5282+ var result = _ibAutomater . Start ( false ) ;
5283+ CheckIbAutomaterError ( result ) ;
52515284
5252- Connect ( ) ;
5285+ // Has error but we are still running, we might be waiting for 2FA user required action after timeout, let's not connect in that case
5286+ if ( ! result . HasError )
5287+ {
5288+ Connect ( ) ;
5289+ }
52535290 }
52545291 catch ( Exception exception )
52555292 {
@@ -5358,6 +5395,11 @@ private DateTime GetNextWeeklyRestartTimeUtc(DateTime currentDate)
53585395
53595396 private void CheckIbAutomaterError ( StartResult result , bool throwException = true )
53605397 {
5398+ if ( IsRecuperable2FATimeout ( result ) )
5399+ {
5400+ return ;
5401+ }
5402+
53615403 if ( result . HasError )
53625404 {
53635405 OnMessage ( new BrokerageMessageEvent ( BrokerageMessageType . Error , result . ErrorCode . ToString ( ) , result . ErrorMessage ) ) ;
@@ -5369,6 +5411,16 @@ private void CheckIbAutomaterError(StartResult result, bool throwException = tru
53695411 }
53705412 }
53715413
5414+ private bool IsRecuperable2FATimeout ( StartResult result )
5415+ {
5416+ if ( _pastFirstConnection && result . ErrorCode == ErrorCode . TwoFactorConfirmationTimeout )
5417+ {
5418+ Log . Trace ( $ "InteractiveBrokersBrokerage.IsRecuperable2FATimeout(): will trigger user action request") ;
5419+ return true ;
5420+ }
5421+ return false ;
5422+ }
5423+
53725424 private void HandleAccountSummary ( object sender , IB . AccountSummaryEventArgs e )
53735425 {
53745426 Log . Trace ( $ "InteractiveBrokersBrokerage.HandleAccountSummary(): Request id: { e . RequestId } , Account: { e . Account } , Tag: { e . Tag } , Value: { e . Value } , Currency: { e . Currency } ") ;
0 commit comments