/// <summary> /// Creates a new IBrokerage instance and set ups the environment for the brokerage /// </summary> /// <param name="job">The job packet to create the brokerage for</param> /// <param name="algorithm">The algorithm instance</param> /// <returns>A new brokerage instance</returns> public override IBrokerage CreateBrokerage(LiveNodePacket job, IAlgorithm algorithm) { var errors = new List <string>(); // read values from the brokerage datas var useTws = Config.GetBool("ib-use-tws"); var port = Config.GetInt("ib-port", 4001); var host = Config.Get("ib-host", "127.0.0.1"); var twsDirectory = Config.Get("ib-tws-dir", "C:\\Jts"); var ibControllerDirectory = Config.Get("ib-controller-dir", "C:\\IBController"); var account = Read <string>(job.BrokerageData, "ib-account", errors); var userID = Read <string>(job.BrokerageData, "ib-user-name", errors); var password = Read <string>(job.BrokerageData, "ib-password", errors); var agentDescription = Read <AgentDescription>(job.BrokerageData, "ib-agent-description", errors); if (errors.Count != 0) { // if we had errors then we can't create the instance throw new Exception(string.Join(Environment.NewLine, errors)); } // launch the IB gateway InteractiveBrokersGatewayRunner.Start(ibControllerDirectory, twsDirectory, userID, password, useTws); var ib = new InteractiveBrokersBrokerage(algorithm.Transactions, algorithm.Portfolio, account, host, port, agentDescription); Composer.Instance.AddPart <IDataQueueHandler>(ib); return(ib); }
public void ClientCancelsLimitOrder() { OrderStatus status = OrderStatus.New; var manualResetEvent = new ManualResetEvent(false); var ib = new InteractiveBrokersBrokerage(); ib.Connect(); ib.OrderEvent += (sender, args) => { status = args.Status; manualResetEvent.Set(); }; // try to sell a single share at a ridiculous price, we'll cancel this later var order = new Order("AAPL", SecurityType.Equity, -1, OrderType.Limit, DateTime.UtcNow, 100000); ib.PlaceOrder(order); manualResetEvent.WaitOne(2500); ib.CancelOrder(order); manualResetEvent.Reset(); manualResetEvent.WaitOne(2500); Assert.AreEqual(OrderStatus.Canceled, status); }
/// <summary> /// Creates a new IBrokerage instance and set ups the environment for the brokerage /// </summary> /// <param name="job">The job packet to create the brokerage for</param> /// <param name="algorithm">The algorithm instance</param> /// <returns>A new brokerage instance</returns> public override IBrokerage CreateBrokerage(LiveNodePacket job, IAlgorithm algorithm) { var errors = new List <string>(); // read values from the brokerage datas var port = Config.GetInt("ib-port", 4001); var host = Config.Get("ib-host", "127.0.0.1"); var twsDirectory = Config.Get("ib-tws-dir", "C:\\Jts"); var ibVersion = Config.Get("ib-version", "974"); var account = Read <string>(job.BrokerageData, "ib-account", errors); var userId = Read <string>(job.BrokerageData, "ib-user-name", errors); var password = Read <string>(job.BrokerageData, "ib-password", errors); var tradingMode = Read <string>(job.BrokerageData, "ib-trading-mode", errors); var agentDescription = Read <string>(job.BrokerageData, "ib-agent-description", errors); var loadExistingHoldings = true; if (job.BrokerageData.ContainsKey("load-existing-holdings")) { loadExistingHoldings = Convert.ToBoolean(job.BrokerageData["load-existing-holdings"]); } if (errors.Count != 0) { // if we had errors then we can't create the instance throw new Exception(string.Join(Environment.NewLine, errors)); } if (tradingMode.IsNullOrEmpty()) { throw new Exception("No trading mode selected. Please select either 'paper' or 'live' trading."); } var ib = new InteractiveBrokersBrokerage( algorithm, algorithm.Transactions, algorithm.Portfolio, Composer.Instance.GetExportedValueByTypeName <IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager")), Composer.Instance.GetExportedValueByTypeName <IMapFileProvider>(Config.Get("map-file-provider", "QuantConnect.Data.Auxiliary.LocalDiskMapFileProvider")), account, host, port, twsDirectory, ibVersion, userId, password, tradingMode, agentDescription, loadExistingHoldings); Composer.Instance.AddPart <IDataQueueHandler>(ib); return(ib); }
public void InitializeBrokerage() { InteractiveBrokersGatewayRunner.Start(Config.Get("ib-controller-dir"), Config.Get("ib-tws-dir"), Config.Get("ib-user-name"), Config.Get("ib-password"), Config.GetBool("ib-use-tws") ); // grabs account info from configuration _interactiveBrokersBrokerage = new InteractiveBrokersBrokerage(new OrderProvider(_orders), new SecurityProvider()); _interactiveBrokersBrokerage.Connect(); }
/// <summary> /// Creates a new IBrokerage instance and set ups the environment for the brokerage /// </summary> /// <param name="job">The job packet to create the brokerage for</param> /// <param name="algorithm">The algorithm instance</param> /// <returns>A new brokerage instance</returns> public override IBrokerage CreateBrokerage(LiveNodePacket job, IAlgorithm algorithm) { var errors = new List <string>(); // read values from the brokerage datas var port = Config.GetInt("ib-port", 4001); var host = Config.Get("ib-host", "127.0.0.1"); var twsDirectory = Config.Get("ib-tws-dir", "C:\\Jts"); var ibVersion = Config.Get("ib-version", "974"); var account = Read <string>(job.BrokerageData, "ib-account", errors); var userId = Read <string>(job.BrokerageData, "ib-user-name", errors); var password = Read <string>(job.BrokerageData, "ib-password", errors); var tradingMode = Read <string>(job.BrokerageData, "ib-trading-mode", errors); var agentDescription = Read <string>(job.BrokerageData, "ib-agent-description", errors); if (errors.Count != 0) { // if we had errors then we can't create the instance throw new Exception(string.Join(Environment.NewLine, errors)); } if (tradingMode.IsNullOrEmpty()) { throw new Exception("No trading mode selected. Please select either 'paper' or 'live' trading."); } var ib = new InteractiveBrokersBrokerage( algorithm, algorithm.Transactions, algorithm.Portfolio, account, host, port, twsDirectory, ibVersion, userId, password, tradingMode, agentDescription); Composer.Instance.AddPart <IDataQueueHandler>(ib); return(ib); }
public void InitializeBrokerage() { InteractiveBrokersGatewayRunner.Start(Config.Get("ib-controller-dir"), Config.Get("ib-tws-dir"), Config.Get("ib-user-name"), Config.Get("ib-password"), Config.Get("ib-trading-mode"), Config.GetBool("ib-use-tws") ); // grabs account info from configuration var securityProvider = new SecurityProvider(); securityProvider[Symbols.USDJPY] = new Security(SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork), new SubscriptionDataConfig(typeof(TradeBar), Symbols.USDJPY, Resolution.Minute, TimeZones.NewYork, TimeZones.NewYork, false, false, false), new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency)); _interactiveBrokersBrokerage = new InteractiveBrokersBrokerage(new OrderProvider(_orders), securityProvider); _interactiveBrokersBrokerage.Connect(); }
private static Order AssertOrderOpened(bool orderFilled, QuantConnect.Brokerages.InteractiveBrokers.InteractiveBrokersBrokerage ib, Order order) { // if the order didn't fill check for it as an open order if (!orderFilled) { // find the right order and return it foreach (var openOrder in ib.GetOpenOrders()) { if (openOrder.BrokerId.Any(id => order.BrokerId.Any(x => x == id))) { return(openOrder); } } Assert.Fail("The order was not filled and was unable to be located via GetOpenOrders()"); } Assert.Pass("The order was successfully filled!"); return(null); }
public void GetsTickData() { InteractiveBrokersGatewayRunner.StartFromConfiguration(); var ib = new InteractiveBrokersBrokerage(new OrderProvider()); ib.Connect(); ib.Subscribe(null, new List<Symbol> {Symbols.USDJPY, Symbols.EURGBP}); Thread.Sleep(1000); for (int i = 0; i < 10; i++) { foreach (var tick in ib.GetNextTicks()) { Console.WriteLine("{0}: {1} - {2} @ {3}", tick.Time, tick.Symbol, tick.Price, ((Tick)tick).Quantity); } } InteractiveBrokersGatewayRunner.Stop(); }
public void ClientPlacesLimitOrder() { bool orderFilled = false; var manualResetEvent = new ManualResetEvent(false); var ib = new InteractiveBrokersBrokerage(); ib.Connect(); decimal aapl = 100m; decimal delta = 85.0m; // if we can't get a price then make the delta huge ib.OrderEvent += (sender, args) => { orderFilled = true; aapl = args.FillPrice; delta = 0.02m; manualResetEvent.Set(); }; // get the current market price, couldn't get RequestMarketData to fire tick events int id = 0; ib.PlaceOrder(new Order("AAPL", SecurityType.Equity, 1, OrderType.Market, DateTime.UtcNow) { Id = ++id }); manualResetEvent.WaitOne(2000); manualResetEvent.Reset(); // make a box around the current price +- a little const int quantity = 1; var order = new Order("AAPL", SecurityType.Equity, +quantity, OrderType.Limit, DateTime.Now, aapl - delta) {Id = ++id}; ib.PlaceOrder(order); ib.PlaceOrder(new Order("AAPL", SecurityType.Equity, -quantity, OrderType.Limit, DateTime.Now, aapl + delta) {Id = ++id}); manualResetEvent.WaitOne(1000); var orderFromIB = AssertOrderOpened(orderFilled, ib, order); Assert.AreEqual(OrderType.Limit, orderFromIB.Type); }
public void GetsTickData() { InteractiveBrokersGatewayRunner.StartFromConfiguration(); var ib = new InteractiveBrokersBrokerage(new OrderMapping()); ib.Connect(); ib.Subscribe(null, new Dictionary<SecurityType, List<string>> { {SecurityType.Forex, new List<string>{"USDJPY", "EURGBP"}} }); Thread.Sleep(1000); for (int i = 0; i < 10; i++) { foreach (var tick in ib.GetNextTicks()) { Console.WriteLine("{0}: {1} - {2} @ {3}", tick.Time, tick.Symbol, tick.Price, ((Tick)tick).Quantity); } } InteractiveBrokersGatewayRunner.Stop(); }
/// <summary> /// Parses a contract for future with malformed data. /// Malformed data usually manifests itself by having "0" assigned to some values /// we expect, like the contract's expiry date. The contract is returned by IB /// like this, usually due to a high amount of data subscriptions that are active /// in an account, surpassing IB's imposed limit. Read more about this here: https://interactivebrokers.github.io/tws-api/rtd_fqa_errors.html#rtd_common_errors_maxmktdata /// /// We are provided a string in the Symbol in malformed contracts that can be /// parsed to construct the clean contract, which is done by this method. /// </summary> /// <param name="malformedContract">Malformed contract (for futures), i.e. a contract with invalid values ("0") in some of its fields</param> /// <param name="symbolPropertiesDatabase">The symbol properties database to use</param> /// <returns>Clean Contract for the future</returns> /// <remarks> /// The malformed contract returns data similar to the following when calling <see cref="InteractiveBrokersBrokerage.GetContractDetails"/>: ES MAR2021 /// </remarks> public Contract ParseMalformedContractFutureSymbol(Contract malformedContract, SymbolPropertiesDatabase symbolPropertiesDatabase) { Log.Trace($"InteractiveBrokersSymbolMapper.ParseMalformedContractFutureSymbol(): Parsing malformed contract: {InteractiveBrokersBrokerage.GetContractDescription(malformedContract)} with trading class: \"{malformedContract.TradingClass}\""); // capture any character except spaces, match spaces, capture any char except digits, capture digits var matches = Regex.Matches(malformedContract.Symbol, @"^(\S*)\s*(\D*)(\d*)"); var match = matches[0].Groups; var contractSymbol = match[1].Value; var contractMonthExpiration = DateTime.ParseExact(match[2].Value, "MMM", CultureInfo.CurrentCulture).Month; var contractYearExpiration = match[3].Value; var leanSymbol = GetLeanRootSymbol(contractSymbol); string market; if (!symbolPropertiesDatabase.TryGetMarket(leanSymbol, SecurityType.Future, out market)) { market = InteractiveBrokersBrokerageModel.DefaultMarketMap[SecurityType.Future]; } var canonicalSymbol = Symbol.Create(leanSymbol, SecurityType.Future, market); var contractMonthYear = new DateTime(int.Parse(contractYearExpiration, CultureInfo.InvariantCulture), contractMonthExpiration, 1); // we get the expiration date using the FuturesExpiryFunctions var contractExpirationDate = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalSymbol)(contractMonthYear); return(new Contract { Symbol = contractSymbol, Multiplier = malformedContract.Multiplier, LastTradeDateOrContractMonth = $"{contractExpirationDate:yyyyMMdd}", Exchange = malformedContract.Exchange, SecType = malformedContract.SecType, IncludeExpired = false, Currency = malformedContract.Currency }); }
/// <summary> /// Parses a contract for options with malformed data. /// Malformed data usually manifests itself by having "0" assigned to some values /// we expect, like the contract's expiry date. The contract is returned by IB /// like this, usually due to a high amount of data subscriptions that are active /// in an account, surpassing IB's imposed limit. Read more about this here: https://interactivebrokers.github.io/tws-api/rtd_fqa_errors.html#rtd_common_errors_maxmktdata /// /// We are provided a string in the Symbol in malformed contracts that can be /// parsed to construct the clean contract, which is done by this method. /// </summary> /// <param name="malformedContract">Malformed contract (for options), i.e. a contract with invalid values ("0") in some of its fields</param> /// <param name="exchange">Exchange that the contract's asset lives on/where orders will be routed through</param> /// <returns>Clean Contract for the option</returns> /// <remarks> /// The malformed contract returns data similar to the following when calling <see cref="InteractiveBrokersBrokerage.GetContractDetails"/>: /// OPT SPY JUN2021 350 P [SPY 210618P00350000 100] USD 0 0 0 /// /// ... which the contents inside [] follow the pattern: /// /// [SYMBOL YY_MM_DD_OPTIONRIGHT_STRIKE(divide by 1000) MULTIPLIER] /// </remarks> public static Contract ParseMalformedContractOptionSymbol(Contract malformedContract, string exchange = "Smart") { Log.Trace($"InteractiveBrokersSymbolMapper.ParseMalformedContractOptionSymbol(): Parsing malformed contract: {InteractiveBrokersBrokerage.GetContractDescription(malformedContract)} with trading class: \"{malformedContract.TradingClass}\""); // we search for the '[ ]' pattern, inside of it we: (capture any character except spaces, match spaces) -> 3 times var matches = Regex.Matches(malformedContract.Symbol, @"^.*[\[](\S*)\s*(\S*)\s*(\S*)[\]]"); var match = matches[0].Groups; var contractSymbol = match[1].Value; var contractSpecification = match[2].Value; var multiplier = match[3].Value; var expiryDate = "20" + contractSpecification.Substring(0, 6); var contractRight = contractSpecification[6] == 'C' ? IB.RightType.Call : IB.RightType.Put; var contractStrike = long.Parse(contractSpecification.Substring(7), CultureInfo.InvariantCulture) / 1000.0; return(new Contract { Symbol = contractSymbol, Multiplier = multiplier, LastTradeDateOrContractMonth = expiryDate, Right = contractRight, Strike = contractStrike, Exchange = exchange, SecType = malformedContract.SecType, IncludeExpired = false, Currency = malformedContract.Currency }); }
private static Order AssertOrderOpened(bool orderFilled, InteractiveBrokersBrokerage ib, Order order) { // if the order didn't fill check for it as an open order if (!orderFilled) { // find the right order and return it foreach (var openOrder in ib.GetOpenOrders()) { if (openOrder.BrokerId.Any(id => order.BrokerId.Any(x => x == id))) { return openOrder; } } Assert.Fail("The order was not filled and was unable to be located via GetOpenOrders()"); } Assert.Pass("The order was successfully filled!"); return null; }
/// <summary> /// Parses a contract for options with malformed data. /// Malformed data usually manifests itself by having "0" assigned to some values /// we expect, like the contract's expiry date. The contract is returned by IB /// like this, usually due to a high amount of data subscriptions that are active /// in an account, surpassing IB's imposed limit. Read more about this here: https://interactivebrokers.github.io/tws-api/rtd_fqa_errors.html#rtd_common_errors_maxmktdata /// /// We are provided a string in the Symbol in malformed contracts that can be /// parsed to construct the clean contract, which is done by this method. /// </summary> /// <param name="malformedContract">Malformed contract (for options), i.e. a contract with invalid values ("0") in some of its fields</param> /// <param name="exchange">Exchange that the contract's asset lives on/where orders will be routed through</param> /// <returns>Clean Contract for the option</returns> /// <remarks> /// The malformed contract returns data similar to the following when calling <see cref="InteractiveBrokersBrokerage.GetContractDetails"/>: /// OPT SPY JUN2021 350 P [SPY 210618P00350000 100] USD 0 0 0 /// /// ... which the contents inside [] follow the pattern: /// /// [SYMBOL YY_MM_DD_OPTIONRIGHT_STRIKE(divide by 1000) MULTIPLIER] /// </remarks> public static Contract ParseMalformedContractOptionSymbol(Contract malformedContract, string exchange = "Smart") { Log.Trace($"InteractiveBrokersSymbolMapper.ParseMalformedContractOptionSymbol(): Parsing malformed contract: {InteractiveBrokersBrokerage.GetContractDescription(malformedContract)} with trading class: \"{malformedContract.TradingClass}\""); var contractInfoSplit = malformedContract.Symbol.Substring(malformedContract.Symbol.IndexOf('[')) .Replace("[", "") .Replace("]", "") .Split(' '); var contractSymbol = contractInfoSplit[0]; var contractSpecification = contractInfoSplit[1]; var multiplier = contractInfoSplit[2]; var expiryDate = "20" + contractSpecification.Substring(0, 6); var contractRight = contractSpecification[6] == 'C' ? IB.RightType.Call : IB.RightType.Put; var contractStrike = long.Parse(contractSpecification.Substring(7), CultureInfo.InvariantCulture) / 1000.0; return(new Contract { Symbol = contractSymbol, Multiplier = multiplier, LastTradeDateOrContractMonth = expiryDate, Right = contractRight, Strike = contractStrike, Exchange = exchange, SecType = malformedContract.SecType, IncludeExpired = false, Currency = malformedContract.Currency }); }
public void ClientConnects() { var ib = new InteractiveBrokersBrokerage(); ib.Connect(); }
public void Teardown() { try { // give the tear down a header so we can easily find it in the logs Log.Trace("-----"); Log.Trace("InteractiveBrokersBrokerageTests.Teardown(): Starting teardown..."); Log.Trace("-----"); var canceledResetEvent = new ManualResetEvent(false); var filledResetEvent = new ManualResetEvent(false); _interactiveBrokersBrokerage.OrderStatusChanged += (sender, orderEvent) => { if (orderEvent.Status == OrderStatus.Filled) { filledResetEvent.Set(); } if (orderEvent.Status == OrderStatus.Canceled) { canceledResetEvent.Set(); } }; // cancel all open orders Log.Trace("InteractiveBrokersBrokerageTests.Teardown(): Canceling open orders..."); var orders = _interactiveBrokersBrokerage.GetOpenOrders(); foreach (var order in orders) { _interactiveBrokersBrokerage.CancelOrder(order); canceledResetEvent.WaitOne(3000); canceledResetEvent.Reset(); } Log.Trace("InteractiveBrokersBrokerageTests.Teardown(): Liquidating open positions..."); // liquidate all positions var holdings = _interactiveBrokersBrokerage.GetAccountHoldings(); foreach (var holding in holdings.Where(x => x.Quantity != 0)) { //var liquidate = new MarketOrder(holding.Symbol, (int) -holding.Quantity, DateTime.UtcNow, type: holding.Type); //_interactiveBrokersBrokerage.PlaceOrder(liquidate); //filledResetEvent.WaitOne(3000); //filledResetEvent.Reset(); } var openOrdersText = _interactiveBrokersBrokerage.GetOpenOrders().Select(x => x.Symbol.ToString() + " " + x.Quantity); Log.Trace("InteractiveBrokersBrokerageTests.Teardown(): Open orders: " + string.Join(", ", openOrdersText)); //Assert.AreEqual(0, actualOpenOrderCount, "Failed to verify that there are zero open orders."); var holdingsText = _interactiveBrokersBrokerage.GetAccountHoldings().Where(x => x.Quantity != 0).Select(x => x.Symbol.ToString() + " " + x.Quantity); Log.Trace("InteractiveBrokersBrokerageTests.Teardown(): Account holdings: " + string.Join(", ", holdingsText)); //Assert.AreEqual(0, holdingsCount, "Failed to verify that there are zero account holdings."); _interactiveBrokersBrokerage.Dispose(); _interactiveBrokersBrokerage = null; } finally { InteractiveBrokersGatewayRunner.Stop(); } }
public void ClientSellsMarketOrder() { bool orderFilled = false; var manualResetEvent = new ManualResetEvent(false); var ib = new InteractiveBrokersBrokerage(); ib.Connect(); ib.OrderEvent += (sender, args) => { orderFilled = true; manualResetEvent.Set(); }; // sell a single share var order = new Order("AAPL", SecurityType.Equity, -1, OrderType.Market, DateTime.UtcNow); ib.PlaceOrder(order); manualResetEvent.WaitOne(2500); var orderFromIB = AssertOrderOpened(orderFilled, ib, order); Assert.AreEqual(OrderType.Market, orderFromIB.Type); }
public void ClientPlacesMarketOrder() { bool orderFilled = false; var manualResetEvent = new ManualResetEvent(false); var ib = new InteractiveBrokersBrokerage(); ib.Connect(); ib.Client.RequestOpenOrders(); ib.OrderEvent += (sender, args) => { orderFilled = true; manualResetEvent.Set(); }; const int buyQuantity = 1; //ib.PlaceOrder(new Order("AAPL", SecurityType.Equity, buyQuantity, OrderType.Market, DateTime.Now)); var order = new Order("AAPL", SecurityType.Equity, buyQuantity, OrderType.Market, DateTime.Now); ib.PlaceOrder(order); manualResetEvent.WaitOne(2500); var orderFromIB = AssertOrderOpened(orderFilled, ib, order); Assert.AreEqual(OrderType.Market, orderFromIB.Type); }
/// <summary> /// Creates a new IBrokerage instance and set ups the environment for the brokerage /// </summary> /// <param name="job">The job packet to create the brokerage for</param> /// <param name="algorithm">The algorithm instance</param> /// <returns>A new brokerage instance</returns> public override IBrokerage CreateBrokerage(LiveNodePacket job, IAlgorithm algorithm) { var errors = new List<string>(); // read values from the brokerage datas var useTws = Config.GetBool("ib-use-tws"); var port = Config.GetInt("ib-port", 4001); var host = Config.Get("ib-host", "127.0.0.1"); var twsDirectory = Config.Get("ib-tws-dir", "C:\\Jts"); var ibControllerDirectory = Config.Get("ib-controller-dir", "C:\\IBController"); var account = Read<string>(job.BrokerageData, "ib-account", errors); var userID = Read<string>(job.BrokerageData, "ib-user-name", errors); var password = Read<string>(job.BrokerageData, "ib-password", errors); var agentDescription = Read<AgentDescription>(job.BrokerageData, "ib-agent-description", errors); if (errors.Count != 0) { // if we had errors then we can't create the instance throw new Exception(string.Join(Environment.NewLine, errors)); } // launch the IB gateway InteractiveBrokersGatewayRunner.Start(ibControllerDirectory, twsDirectory, userID, password, useTws); var ib = new InteractiveBrokersBrokerage(algorithm.Transactions, algorithm.Portfolio, account, host, port, agentDescription); Composer.Instance.AddPart<IDataQueueHandler>(ib); return ib; }