/// <summary> /// Estimate the profit from advices produced by analysis process. /// The function will produce a list of "transactions" assuming to be done from analysis advices. /// </summary> /// <param name="data"> Data used for analysis </param> /// <param name="tradePoints">Trade point list generated by analysis process</param> /// <param name="options">User- specific options : captital, max Buy...</param> /// <param name="returnObj">Returned object </param> /// <param name="afterEachEstimationFunc">Call-back function at the end of each tradepoind estimation</param> /// <param name="afterEstimationFunc">Call-back function at the end of estimation process</param> /// public static void EstimateTrading(AnalysisData data, TradePointInfo[] tradePoints, EstimateOptions options, object returnObj, AfterEachEstimationFunc afterEachEstimationFunc, AfterEstimationFunc afterEstimationFunc) { EstimationData myEstimationData = new EstimationData(); global::data.baseDS.stockExchangeRow marketRow = application.AppLibs.GetStockExchange(data.DataStockCode); decimal initCapAmt = options.TotalCapAmt * options.MaxBuyAmtPerc / 100; decimal priceWeight = marketRow.priceRatio; decimal feePerc = marketRow.tranFeePerc / 100; short buy2SellInterval = marketRow.minBuySellDay; data.baseDS.stockCodeRow stockCodeRow = application.SysLibs.FindAndCache_StockCode(data.DataStockCode); if (stockCodeRow == null) { return; } int adviceDataIdx, lastBuyId = -1; decimal stockQty = 0, qty; decimal maxBuyQty = (decimal)(stockCodeRow.noOutstandingStock * options.MaxBuyQtyPerc / 100); decimal stockAmt = 0, stockPrice = 0, amt, feeAmt, totalFeeAmt = 0; decimal cashAmt = initCapAmt; DateTime transDate = common.Consts.constNullDate;; for (int idx = 0; idx < tradePoints.Length; idx++) { adviceDataIdx = tradePoints[idx].DataIdx; qty = 0; amt = 0; myEstimationData.ignored = false; AppTypes.TradeActions action = tradePoints[idx].TradeAction; stockPrice = (decimal)data.Close[adviceDataIdx]; transDate = DateTime.FromOADate(data.DateTime[adviceDataIdx]); switch (action) { case AppTypes.TradeActions.Buy: //Assume that we can only buy if we have money qty = (stockPrice == 0 ? 0 : Math.Floor(cashAmt / ((stockPrice * priceWeight) * (1 + feePerc)))); if (qty > maxBuyQty) { qty = maxBuyQty; } if (qty != 0) { amt = qty * stockPrice * priceWeight; stockAmt += amt; stockQty += qty; feeAmt = Math.Round(amt * feePerc, 0); cashAmt -= amt + feeAmt; totalFeeAmt += feeAmt; lastBuyId = adviceDataIdx; } else { myEstimationData.ignored = true; } break; case AppTypes.TradeActions.Sell: //Can sell if own some stock if (stockQty <= 0) { myEstimationData.ignored = true; break; } // Not applicable to sell if (lastBuyId < 0) { myEstimationData.ignored = true; break; } // T+4 contrainst ? if (common.dateTimeLibs.DateDiffInDays(DateTime.FromOADate(data.DateTime[lastBuyId]).Date, DateTime.FromOADate(data.DateTime[adviceDataIdx]).Date) < buy2SellInterval) { // Keep inapplicable Sells ?? if (commonClass.Settings.sysKeepInApplicableSell) { int transDataIdx = -1; DateTime minAllowSellDate = DateTime.FromOADate(data.DateTime[lastBuyId]).Date.AddDays(buy2SellInterval); //If it is the last trade point, find the next applicable date if (idx >= tradePoints.Length - 1) { transDataIdx = FindDateIdx(data, tradePoints[idx].DataIdx + 1, data.DateTime.Count - 1, minAllowSellDate); } else { //If the next trade point is before or at [minAllowSellDate], ignore this if (DateTime.FromOADate(data.DateTime.Values[tradePoints[idx + 1].DataIdx]).Date <= minAllowSellDate) { myEstimationData.ignored = true; } else { //Find the next applicable date after this point and before next point transDataIdx = FindDateIdx(data, tradePoints[idx].DataIdx + 1, tradePoints[idx + 1].DataIdx - 1, minAllowSellDate); } } if (transDataIdx < 0) { myEstimationData.ignored = true; } else { stockPrice = (decimal)data.Close[transDataIdx]; transDate = DateTime.FromOADate(data.DateTime[transDataIdx]).Date; } } else { myEstimationData.ignored = true; } } //Ok, sell it if (!myEstimationData.ignored) { qty = stockQty; amt = qty * stockPrice * priceWeight; stockQty = 0; stockAmt = 0; feeAmt = Math.Round(amt * feePerc, 0); cashAmt += amt - feeAmt; totalFeeAmt += feeAmt; } break; } myEstimationData.tradeAction = action.ToString(); myEstimationData.onDate = transDate; myEstimationData.price = stockPrice; myEstimationData.qty = qty; myEstimationData.amt = amt; myEstimationData.feeAmt = totalFeeAmt; myEstimationData.ownedQty = stockQty; myEstimationData.ownedAmt = stockAmt; myEstimationData.cashAmt = cashAmt; myEstimationData.profitAmt = cashAmt + stockAmt - initCapAmt; if (afterEachEstimationFunc != null) { afterEachEstimationFunc(myEstimationData, returnObj); } } if (afterEstimationFunc != null) { afterEstimationFunc(myEstimationData, returnObj); } }
/// <summary> /// Estimate the profit from advices produced by analysis process. /// The function will produce a list of "transactions" assuming to be done from analysis advices. /// </summary> /// <param name="data"> Data used for analysis </param> /// <param name="tradePoints">Trade point list generated by analysis process</param> /// <param name="options">User- specific options : captital, max Buy...</param> /// <param name="returnObj">Returned object </param> /// <param name="afterEachEstimationFunc">Call-back function at the end of each tradepoind estimation</param> /// <param name="afterEstimationFunc">Call-back function at the end of estimation process</param> /// public static void EstimateTrading(AnalysisData data, TradePointInfo[] tradePoints, EstimateOptions options, object returnObj, AfterEachEstimationFunc afterEachEstimationFunc, AfterEstimationFunc afterEstimationFunc) { EstimationData myEstimationData = new EstimationData(); global::databases.baseDS.stockExchangeRow marketRow = databases.DbAccess.GetStockExchange(data.DataStockCode); decimal initCapAmt = options.TotalCapAmt * options.MaxBuyAmtPerc / 100; decimal priceWeight = marketRow.priceRatio; decimal feePerc = marketRow.tranFeePerc / 100; short buy2SellInterval = marketRow.minBuySellDay; databases.baseDS.stockCodeRow stockCodeRow = application.SysLibs.FindAndCache_StockCode(data.DataStockCode); if (stockCodeRow == null) { return; } int transDataIdx, lastBuyId = -1; decimal stockQty = 0, qty; decimal maxBuyQty = (decimal)(stockCodeRow.noOutstandingStock * options.MaxBuyQtyPerc / 100); decimal stockAmt = 0, stockPrice = 0, amt, feeAmt, totalFeeAmt = 0; decimal cashAmt = initCapAmt; //DateTime transDate = common.Consts.constNullDate; ; for (int idx = 0; idx < tradePoints.Length; idx++) { transDataIdx = tradePoints[idx].DataIdx; qty = 0; amt = 0; myEstimationData.ignored = false; stockPrice = (decimal)data.Close[transDataIdx]; switch (tradePoints[idx].TradeAction) { case AppTypes.TradeActions.Buy: //Assume that we can only buy if we have money qty = (stockPrice == 0 ? 0 : Math.Floor(cashAmt / ((stockPrice * priceWeight) * (1 + feePerc)))); if (qty > maxBuyQty) { qty = maxBuyQty; } if (qty != 0) { amt = qty * stockPrice * priceWeight; stockAmt += amt; stockQty += qty; feeAmt = Math.Round(amt * feePerc, 0); cashAmt -= amt + feeAmt; totalFeeAmt += feeAmt; lastBuyId = transDataIdx; } else { myEstimationData.ignored = true; } break; case AppTypes.TradeActions.Sell: //Can sell if own some stock if (stockQty <= 0) { myEstimationData.ignored = true; break; } // Not applicable to sell if (lastBuyId < 0) { myEstimationData.ignored = true; break; } //========================== // Check T+4 contrainst //========================== int minAllowSellPointIdx = lastBuyId + buy2SellInterval; // [minAllowSellPoint] is out of data bound , ignore it. if (minAllowSellPointIdx >= data.DateTime.Count) { myEstimationData.ignored = true; } // Violate T4 contrainst ? if (!myEstimationData.ignored && tradePoints[idx].DataIdx < minAllowSellPointIdx) { // Keep inapplicable Sells ?? if (Settings.sysKeepInApplicableSell) { //If it is the last trade point, make transaction (sell) at [minAllowSellPoint] if (idx >= tradePoints.Length - 1) { transDataIdx = minAllowSellPointIdx; } else { //If there is some trade point between it and [minAllowSellPoint], ignore it if (tradePoints[idx + 1].DataIdx < minAllowSellPointIdx) { myEstimationData.ignored = true; } else { transDataIdx = minAllowSellPointIdx; } } } else { myEstimationData.ignored = true; } } //Ok, sell it if (myEstimationData.ignored != true) { stockPrice = (decimal)data.Close[transDataIdx]; qty = stockQty; amt = qty * stockPrice * priceWeight; stockQty = 0; stockAmt = 0; feeAmt = Math.Round(amt * feePerc, 0); cashAmt += amt - feeAmt; totalFeeAmt += feeAmt; //Adjust trade point to refresh chages by T4 constrainst tradePoints[idx].DataIdx = transDataIdx; } else { tradePoints[idx].isValid = false; } break; } myEstimationData.tradeAction = tradePoints[idx].TradeAction; myEstimationData.onDate = DateTime.FromOADate(data.DateTime[transDataIdx]); myEstimationData.price = stockPrice; myEstimationData.qty = qty; myEstimationData.amt = amt; myEstimationData.feeAmt = totalFeeAmt; myEstimationData.ownedQty = stockQty; myEstimationData.ownedAmt = stockAmt; myEstimationData.cashAmt = cashAmt; myEstimationData.profitAmt = cashAmt + stockAmt - initCapAmt; if (afterEachEstimationFunc != null) { afterEachEstimationFunc(myEstimationData, returnObj); } } if (afterEstimationFunc != null) { afterEstimationFunc(myEstimationData, returnObj); } }