Skip to content

Commit 986e697

Browse files
Feature 2FA timeout retry handling (#199)
* TEST * Some cleanup * Update QuantConnect.InteractiveBrokersBrokerage.csproj * Update InteractiveBrokersBrokerage.cs --------- Co-authored-by: Jhonathan Abreu <[email protected]>
1 parent 0c0071a commit 986e697

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

QuantConnect.InteractiveBrokersBrokerage/InteractiveBrokersBrokerage.cs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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}");

QuantConnect.InteractiveBrokersBrokerage/QuantConnect.InteractiveBrokersBrokerage.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<ItemGroup>
3333
<PackageReference Include="QuantConnect.Brokerages" Version="2.5.*" />
3434
<PackageReference Include="QuantConnect.Lean.Engine" Version="2.5.*" />
35-
<PackageReference Include="QuantConnect.IBAutomater" Version="2.0.85">
35+
<PackageReference Include="QuantConnect.IBAutomater" Version="2.0.88">
3636
<!--
3737
Content files of dependencies are excluded by default.
3838
This allows content files to be available in project where nuget will be consumed.
@@ -59,4 +59,4 @@
5959
</None>
6060
</ItemGroup>
6161

62-
</Project>
62+
</Project>

0 commit comments

Comments
 (0)