示例#1
0
        private bool WaitTransactionsViaBrokerAndCollectExecutionInfo(BrokerTaskPortfolio p_portfolio, StringBuilder p_errorStr)
        {
            var transactions = p_portfolio.ProposedTransactions;
            if (transactions.Count == 0)
                return true;

            bool isSimulatedTrades = (bool)Trigger.TriggerSettings[BrokerTaskSetting.IsSimulatedTrades];       // simulation settings is too important to be forgotten to set. If it is not in settings, this will throw exception. OK.
            List<Task> tasks = new List<Task>();
            for (int i = 0; i < transactions.Count; i++)
            {
                Task task = Task.Factory.StartNew((transaction) =>
                {
                    Controller.g_gatewaysWatcher.WaitOrder(p_portfolio.IbGatewayUserToTrade, ((Transaction)transaction).VirtualOrderId, isSimulatedTrades);
                }, transactions[i]);
                tasks.Add(task);
            }

            //Block until all transactions complete.
            Utils.Logger.Info("Before Task.WaitAllOrders(tasks)");
            // http://stackoverflow.com/questions/9846615/async-task-whenall-with-timeout
            // Timeout here is not necessary, because BrokerWrapper.WaitOrder() will do a 2 minutes timeout for MKT orders, and Properly calculated MarketClose+2 minutes for MOC orders.
            // However, for absolute safety, to send Error Email to Supervisor, don't let an inifinte Task.WaitAll() here.
            // Imagine a case when MOC order was done at 9:30. Until 16:00, there is 6:30. So, do a 7hours timeout here.
            var allTasks = Task.WhenAll(tasks.ToArray());
            var theFirstCompletedTask = Task.WhenAny(allTasks, Task.Delay(TimeSpan.FromHours(7))).Result;   // returns false if timeout
            Utils.Logger.Info($"After Task.WaitAllOrders(tasks). Note: BrokerWrapper.WaitOrder() will do a 2 minutes timeout (MKT, MOC). Was it 7 hours timeout: {theFirstCompletedTask != allTasks} (Good, if there was no order timeout, but it is not a guarantee that everything was right. For example. Shares were not available for shorting.");

            bool wasAnyErrorInOrders = false;
            for (int i = 0; i < transactions.Count; i++)
            {
                var transaction = transactions[i];
                OrderStatus orderStatus = OrderStatus.None; // a Property cannot be passed to a ref parameter, so we have to use temporary variables
                double executedVolume = Double.NaN;
                double executedAvgPrice = Double.NaN;
                DateTime executionTime = DateTime.UtcNow;
                if (!Controller.g_gatewaysWatcher.GetVirtualOrderExecutionInfo(p_portfolio.IbGatewayUserToTrade, transaction.VirtualOrderId, ref orderStatus, ref executedVolume, ref executedAvgPrice, ref executionTime, isSimulatedTrades))
                {
                    wasAnyErrorInOrders = true;
                    p_errorStr.AppendLine($"GetVirtualOrderExecutionInfo() failed for virtualOrderId({transaction.VirtualOrderId}): {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume}.");
                }
                else
                {
                    transaction.OrderStatus = orderStatus;
                    transaction.ExecutedVolume = executedVolume;
                    transaction.ExecutedAvgPrice = executedAvgPrice;
                    transaction.DateTime = executionTime;
                    if (transaction.OrderStatus == OrderStatus.MinFilterSkipped)
                    {
                        Utils.Logger.Info($"Ok. {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume}. Transaction.OrderStatus != OrderStatus.Filled. It is {transaction.OrderStatus}");       // This is Info. expected
                    }
                    else if (transaction.OrderStatus == OrderStatus.MaxFilterSkipped)
                    {
                        wasAnyErrorInOrders = true;
                        p_errorStr.AppendLine($"Error. {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume}. Transaction.OrderStatus != OrderStatus.Filled. It is {transaction.OrderStatus}");       // This is Warn. not expected
                    }
                    else
                    {
                        if (Utils.IsNear(transaction.Volume, transaction.ExecutedVolume))
                        {
                            // everything is OK. OrderStatus should be Filled or Partially Filled.
                            if (transaction.OrderStatus != OrderStatus.Filled)
                            {
                                Utils.Logger.Warn($"{transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume}. Transaction.OrderStatus != OrderStatus.Filled. It is {transaction.OrderStatus}. Force it to be Filled.");
                                transaction.OrderStatus = OrderStatus.Filled;
                            }
                        }
                        else
                        {
                            wasAnyErrorInOrders = true;
                            p_errorStr.AppendLine($"ERROR in {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume},  VirtualOrderId {transaction.VirtualOrderId}: transaction.OrderStatus != OrderStatus.Filled. It is {transaction.OrderStatus}");       // This is Error. not expected
                        } // else
                    }
                }   // else

            }   // for

            return !wasAnyErrorInOrders;
        }   // WaitTransactionsViaBrokerAndCollectExecutionInfo()
示例#2
0
        private void PlaceTransactionsViaBroker(BrokerTaskPortfolio p_portfolio, StringBuilder p_detailedReportSb)
        {
            var transactions = p_portfolio.ProposedTransactions;
            if (transactions.Count == 0)
            {
                Utils.ConsoleWriteLine(ConsoleColor.Green, false, $"***Trades: none.");
                Utils.Logger.Info($"***Trades: none.");
                p_detailedReportSb.AppendLine($"<font color=\"#10ff10\">***Trades: none.</font>");
                return;
            }

            // OPG order: tried in IB: puting 2 minutes before open: result: invalid order (after 10 sec); 3 minutes before open: it was successfull
            // in theory: "Market on open orders must be placed at least 20 minutes", but IB doesn't specify the time.
            // apparently ,the exchanges close the time window 2 minutes before market open. So, if IB is quick enough, it can accept trades even in the last 5 minutes (once I tried with 3 minutes manually, and was successfull)
            // so: with IB: try: 5 minutes, and increase it 1 minute every time it failed. (at the end, we will get to 20 minutes). It may depend on the stock exchange. There are more lenient stock exchanges
            // An MOC (Market on Close) order must be  submitted no later than 15  minutes prior to the close of  the market.
            object orderExecutionObj = BrokerTaskSchema.Settings[BrokerTaskSetting.OrderExecution]; // "MKT", "LMT", "MOC"
            OrderExecution orderExecution = (orderExecutionObj != null) ? (OrderExecution)orderExecutionObj : OrderExecution.Market;    // Market is the default
            object orderTifObj = null;
            OrderTimeInForce orderTif = (BrokerTaskSchema.Settings.TryGetValue(BrokerTaskSetting.OrderTimeInForce, out orderTifObj)) ? (OrderTimeInForce)orderTifObj : OrderTimeInForce.Day;     // Day is the default
            StrongAssert.True(orderExecution == OrderExecution.Market || orderExecution == OrderExecution.MarketOnClose, Severity.ThrowException, $"Non supported OrderExecution: {orderExecution}");
            StrongAssert.True(orderTif == OrderTimeInForce.Day, Severity.ThrowException, $"Non supported OrderTimeInForce: {orderTif}");

            bool isSimulatedTrades = (bool)Trigger.TriggerSettings[BrokerTaskSetting.IsSimulatedTrades];       // simulation settings is too important to be forgotten to set. If it is not in settings, this will throw exception. OK.
            for (int i = 0; i < transactions.Count; i++)    // quickly place the orders. Don't do any other time consuming work here.
            {
                var transaction = transactions[i];
                Utils.Logger.Info($"Placing Order {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume} ");
                Contract contract = new Contract() { Symbol = Strategy.StockIdToTicker(transaction.SubTableID), SecType = "STK", Currency = "USD", Exchange = "SMART" };
                transaction.VirtualOrderId = Controller.g_gatewaysWatcher.PlaceOrder(p_portfolio.IbGatewayUserToTrade, p_portfolio.MaxTradeValueInCurrency, p_portfolio.MinTradeValueInCurrency, contract, transaction.TransactionType, transaction.Volume, orderExecution, orderTif, null, null, isSimulatedTrades, p_detailedReportSb);
            } // don't do anything here. Return, so other portfolio PlaceOrder()-s can be executed too.
        }
示例#3
0
        private void DetermineProposedPositions(BrokerTaskPortfolio p_portfolio, List<PortfolioPositionSpec> p_proposedPositionSpecs)
        {
            double leverage = Strategy.GetPortfolioLeverage(p_proposedPositionSpecs, p_portfolio.Param);
            double totalRiskedCapital = p_portfolio.PortfolioUsdSize * leverage;
            var rtPrices = new Dictionary<int, PriceAndTime>() { { TickType.MID, new PriceAndTime() } };   // MID is the most honest price. LAST may happened 1 hours ago
            p_portfolio.ProposedPositions = p_proposedPositionSpecs.Select(r =>
            {
                int volume = 0;
                if (r.Size is FixedSize)
                    volume = (int)(r.Size as FixedSize).Size * (r.IsShort ? -1 : 1);
                else
                {
                    Contract contract = new Contract() { Symbol = r.Ticker, SecType = "STK", Currency = "USD", Exchange = "SMART" };
                    double rtPrice = 0.0;
                    StrongAssert.True(Controller.g_gatewaysWatcher.GetMktDataSnapshot(contract, ref rtPrices), Severity.ThrowException, $"Warning. Realtime price for {r.Ticker} is misssing. For safety reasons, we can use volume=0 as targetVolume, but it means we would sell the current positions. Better to not continue.");
                    rtPrice = rtPrices[TickType.MID].Price;
                    volume = (int)((r.Size as WeightedSize).Weight * totalRiskedCapital / rtPrice) * (r.IsShort ? -1 : 1);
                }

                return new PortfolioPosition()
                {
                    AssetID = Strategy.TickerToAssetID(r.Ticker),
                    Volume = volume
                };
            }).ToList();

            foreach (var suggestedItem in p_portfolio.ProposedPositions)
            {
                Utils.ConsoleWriteLine(null, false, $"Portfolio suggestion: {Strategy.StockIdToTicker(suggestedItem.SubTableID)}: signed vol: {suggestedItem.Volume}");
                Utils.Logger.Info($"Portfolio suggestion: {Strategy.StockIdToTicker(suggestedItem.SubTableID)}: signed vol: {suggestedItem.Volume}");
            }
        }
示例#4
0
        private void DetermineProposedTransactions(BrokerTaskPortfolio p_portfolio)
        {
            List<Transaction> transactions = new List<Transaction>();
            // 1. close old positions
            //var closedPositions = p_previousPidsWithoutCash.Where(r => !p_targetPortfolio.Select(q => q.AssetID).Contains(r.AssetID));
            foreach (var todayPos in p_portfolio.TodayPositions)
            {
                if (todayPos.AssetTypeID == AssetType.HardCash)
                    continue;   // skip cash positions
                //Utils.Logger.Warn($"Prev Item {Strategy.StockIdToTicker(todayPos.SubTableID)}: {todayPos.Volume} ");

                var proposed = p_portfolio.ProposedPositions.FirstOrDefault(r => r.AssetID == todayPos.AssetID);
                if (proposed == null)   // if an assetID is only in todayPos, it is here
                {
                    transactions.Add(new Transaction()
                    {
                        AssetID = todayPos.AssetID,
                        Volume = Math.Abs(todayPos.Volume),     // Volume should be always positive
                        TransactionType = (todayPos.Volume > 0) ? TransactionType.SellAsset : TransactionType.BuyAsset // it was Cover instead of Buy, But we decided to simplify and allow Buy
                    });
                }
                else
                {   // if an assetID is both in todayPos and in proposedPos, it is here
                    double diffVolume = proposed.Volume - todayPos.Volume; 
                    if (!Utils.IsNearZero(diffVolume))    // don't insert transaction, if volume will be 0
                    {
                        transactions.Add(new Transaction()
                        {
                            AssetID = todayPos.AssetID,
                            Volume = Math.Abs(diffVolume),     // Volume should be always positive
                            TransactionType = (diffVolume > 0) ? TransactionType.BuyAsset : TransactionType.SellAsset
                        });
                    }
                }
            }

            // 2. open new positions:
            foreach (var proposedPos in p_portfolio.ProposedPositions)
            {
                //Utils.Logger.Warn($"New Item {Strategy.StockIdToTicker(proposedPos.SubTableID)}: {proposedPos.Volume} ");
                var todayPos = p_portfolio.TodayPositions.FirstOrDefault(r => r.AssetID == proposedPos.AssetID);
                if (todayPos == null)   // // if an assetID is only in proposedPos, it is here
                {
                    transactions.Add(new Transaction()
                    {
                        AssetID = proposedPos.AssetID,
                        Volume = Math.Abs(proposedPos.Volume),     // Volume should be always positive
                        TransactionType = (proposedPos.Volume > 0) ? TransactionType.BuyAsset : TransactionType.SellAsset // it was Cover instead of Buy, But we decided to simplify and allow Buy
                    });
                }
            }

            foreach (var transaction in transactions)
            {
                Utils.ConsoleWriteLine(ConsoleColor.Green, false, $"***Proposed transaction: {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume} ");
                Utils.Logger.Info($"***Proposed transaction: {transaction.TransactionType} {Strategy.StockIdToTicker(transaction.SubTableID)}: {transaction.Volume} ");
            }

            p_portfolio.ProposedTransactions = transactions;    // only assign at the end, if everything was right, there was no thrown Exception. It is safer to do transactions: all or nothing, not partial
        }
示例#5
0
        public void CalculatePortfolioUsdSizeFromRealTime(BrokerTaskPortfolio p_portfolio)
        {
            var rtPrices = new Dictionary<int, PriceAndTime>() { { TickType.MID, new PriceAndTime() } };    // MID is the most honest price. LAST may happened 1 hours ago

            double portfolioUsdSize = 0;
            foreach (PortfolioPosition pip in p_portfolio.TodayPositions)
            {
                if (pip.AssetTypeID == AssetType.HardCash)
                {
                    if ((CurrencyID)pip.SubTableID == CurrencyID.USD)
                        portfolioUsdSize += pip.Volume; // for Cash, the Volume = 1 in the SQL DB, but we store the Value in the Volume, not in the Price, because of possible future other currencies 
                    else
                        throw new Exception("Only USD is implemented"); // in the future may use fixed or estimated or real time Forex prices
                }
                else
                {
                    int stockID = pip.SubTableID;
                    // TODO: we need a tickerProvider here
                    Contract contract = new Contract() { Symbol = Strategy.StockIdToTicker(stockID), SecType = "STK", Currency = "USD", Exchange = "SMART" };
                    double rtPrice = 0.0;
                    StrongAssert.True(Controller.g_gatewaysWatcher.GetMktDataSnapshot(contract, ref rtPrices), Severity.ThrowException, "There is no point continuing if portfolioUSdSize cannot be calculated. After that we cannot calculate new stock Volumes from weights.");
                    rtPrice = rtPrices[TickType.MID].Price;

                    //double rtPrice = GetAssetIDRealTimePrice(BrokerTask.TaskLogFile, p_brokerAPI, pip.AssetID); 
                    portfolioUsdSize += pip.Volume * rtPrice;  // pip.Volume is signed. For shorts, it is negative, but that is OK.
                }
            }
            p_portfolio.PortfolioUsdSize = portfolioUsdSize;
            Utils.ConsoleWriteLine(null, false, $"Portfolio ({p_portfolio.PortfolioID}) $size (realtime): ${p_portfolio.PortfolioUsdSize:F2}");
            Utils.Logger.Info($"!!!Portfolio ({p_portfolio.PortfolioID}) $size (realtime): ${p_portfolio.PortfolioUsdSize:F2}");
        }