/***************************************************************************** * FUNCTION: RunAnalysis * Description: * Parameters: None *****************************************************************************/ public AnalysisResult RunAnalysis() { int i; bool buy, sell; string str_expression, str_evaluated_rule; double gain_loss = 0.0, cash = inPrincipalAmt, investments = 0.0, transaction_amt = 0.0; int units = 0, units_held = 0, total_transactions = 0; String[] buy_conditions = inBuyRule.Split(RuleParserInputs.Operators, StringSplitOptions.RemoveEmptyEntries); String[] sell_conditions = inSellRule.Split(RuleParserInputs.Operators, StringSplitOptions.RemoveEmptyEntries); AnalysisResult analysis_result = new AnalysisResult(); PercentComplete = 0.0; //Get the list of functions passed in the current Buy and Sell rules //(so that we don't have to itterate again for each data point) current_buy_functions.Clear(); current_sell_functions.Clear(); foreach (Fn func in RuleParserInputs.Fns) { foreach (String buy_cond in buy_conditions) { if (buy_cond.Contains(StringEnum.GetStringValue(func))) { current_buy_functions.Add(func); } } foreach (String sell_cond in sell_conditions) { if (sell_cond.Contains(StringEnum.GetStringValue(func))) { current_sell_functions.Add(func); } } } //ex. ((MACD_DIFF > 0) AND (P < AVG[P][-5..0])) //For every day in data for (i = 0; i < inEquity.HistoricalPriceDate.Count; i++) { //Initialize analysis result values analysis_result.cash_totals.Add(0.0); analysis_result.investments_totals.Add(0.0); analysis_result.net_change_daily.Add(0.0); analysis_result.units_change_daily.Add(0); //Evaluate the given historical data point against each of the buy rules buy = false; str_evaluated_rule = inBuyRule; //Get the number of units to buy units = GetNumberOfUnits(str_evaluated_rule, out str_evaluated_rule, inEquity.HistoricalPrice[i], cash); //Parse each of the individual conditional expressions and evaluate the overall rule foreach (String buy_cond in buy_conditions) { str_expression = Parse(buy_cond, inEquity, i, current_buy_functions); str_evaluated_rule = str_evaluated_rule.Replace(buy_cond, str_expression); } buy = Evaluate(str_evaluated_rule); //Update holdings if the buy rule evaluates to True if (buy) { transaction_amt = inEquity.HistoricalPrice[i] * units; if (cash >= (transaction_amt)) { units_held += units; cash -= transaction_amt; investments = (inEquity.HistoricalPrice[i] * units_held); total_transactions++; } } //Evaluate the given historical data point against each of the sell rules sell = false; str_evaluated_rule = inSellRule; //Get the number of units to sell units = GetNumberOfUnits(str_evaluated_rule, out str_evaluated_rule, 1.0, investments); foreach (String sell_cond in sell_conditions) { str_expression = Parse(sell_cond, inEquity, i, current_sell_functions); str_evaluated_rule = str_evaluated_rule.Replace(sell_cond, str_expression); } sell = Evaluate(str_evaluated_rule); if (sell) { transaction_amt = inEquity.HistoricalPrice[i] * units; if (investments >= transaction_amt) { units_held -= units; cash += transaction_amt; investments = (inEquity.HistoricalPrice[i] * units_held); total_transactions++; units = 0; } } //update Analyis result structure analysis_result.cash_totals[i] = cash; analysis_result.investments_totals[i] = investments; analysis_result.net_change_daily[i] = (cash + investments); analysis_result.units_change_daily[i] = units_held; analysis_result.dates_from_to = new Tuple <DateTime, DateTime>(inEquity.HistoricalPriceDate[0], inEquity.HistoricalPriceDate[inEquity.HistoricalPriceDate.Count - 1]); if (i > 0) { analysis_result.net_change_daily[i] -= (analysis_result.cash_totals[i - 1] + analysis_result.investments_totals[i - 1]); analysis_result.units_change_daily[i] -= analysis_result.units_change_daily[i - 1]; } //Update progress PercentComplete = ((double)(i + 1) / (double)inEquity.HistoricalPriceDate.Count); } PercentComplete = 1.0; gain_loss = (cash + investments) - inPrincipalAmt; analysis_result.net_change = gain_loss; return(analysis_result); }
private int ExpressionEndIndex(string pRuleString, int pFromIndex) { int nesting_level = 0, param_level = 0; int index1 = 0; string tempStr = ""; bool isfound = false; //find the end of the expression nesting_level = 0; param_level = 0; isfound = false; index1 = pFromIndex; while (index1 < pRuleString.Length) { switch (pRuleString[index1]) { case '(': nesting_level++; break; case ')': nesting_level--; break; case '[': param_level++; break; case ']': param_level--; break; default: break; } tempStr = pRuleString.SubWord(index1); try { double.Parse(tempStr); isfound = true; index1 += tempStr.Length; } catch { if (RuleParserInputs.Fns.Where(a => StringEnum.GetStringValue(a) == tempStr).Count() > 0) { isfound = true; index1 += tempStr.Length - 1; } else if (RuleParserInputs.VarList.Where(a => StringEnum.GetStringValue(a) == tempStr).Count() > 0 && (param_level == 0)) { isfound = true; index1 += tempStr.Length - 1; } } if (isfound && (param_level == 0) && (nesting_level == 0)) { //look ahead to the next character in case a valid fn or param string is found // to ensure the whole parameter is taken if (index1 < (pRuleString.Length - 1) && pRuleString[index1 + 1] == '[') { param_level++; } else { break; } } index1++; } return(index1); }
/***************************************************************************** * FUNCTION: Parse * Description: Replaces a primitive expression with corresponding numeric values * to enable evaluation. * * Ex. (P < AVG[P][-5..0]) * Parameters: * *****************************************************************************/ private String Parse(String pExpression, Equity pEqIn, int pIndex, List <Fn> pFNs) { string parameter, str_expression, fn_str; double parameter_value = 0.0; string[] split_str; int index1, index2; Variable in_param; str_expression = pExpression; //For each function in the expression being parsed foreach (Fn func in pFNs) { fn_str = ""; if (pExpression.Contains(StringEnum.GetStringValue(func))) { //Get the substring containing only the logical expression for this function index1 = pExpression.IndexOf(StringEnum.GetStringValue(func)); index2 = pExpression.IndexOf("]", index1); if (pExpression.Length > index2 && pExpression.ToCharArray()[index2 + 1] == '[') { index2 = pExpression.IndexOf("]", index2 + 1); } fn_str = pExpression.Substring(index1, index2 - index1 + 1); try { //Evaluate the function and replace the substring in the original parent expression with the value str_expression = str_expression.Replace(fn_str.Trim(), GetFunction(func, pEqIn, fn_str, pIndex).ToString()); } catch (IndexOutOfRangeException) { //The index will be out of range even for a valid command if the function operates on past data that // isn't available at the start of a series. // // Ex. AVG[P][-5..0] --> at the start of the price data series, there aren't 5 previous data points available // to calculate an average on. // // In this case, just return false to allow the rest of the command to be processed. return("false"); } catch (Exception) { errorIndex[0] = index1; errorIndex[1] = index2; return("ERROR"); } } } split_str = str_expression.Split(RuleParserInputs.Comparators, StringSplitOptions.RemoveEmptyEntries); foreach (string exp_part in split_str) { parameter = CleanExpression(exp_part.Trim()).Replace("NOT", ""); if (Helpers.ValidateNumeric(parameter) == false) { in_param = GetParameterType(parameter); try { //If the data to be analyzed is one of the MACD signals, ensure the passed index is valid if (in_param == Variable.MACD_DIFF || in_param == Variable.MACD_SIG) { if (pIndex > (pEqIn.HistoricalPrice.Count() - pEqIn.MACD_C.Count())) { parameter_value = GetParameter(pEqIn, parameter, pIndex); str_expression = str_expression.ReplaceFirstOccurrence(parameter, parameter_value.ToString()); } } else { parameter_value = GetParameter(pEqIn, parameter, pIndex); str_expression = str_expression.ReplaceFirstOccurrence(parameter, parameter_value.ToString()); } } catch (IndexOutOfRangeException) { return("false"); } catch (Exception) { errorIndex[0] = 0; errorIndex[1] = 0; return("ERROR"); } } } return(str_expression); }
private String PreprocessRule(String pRuleString) { string subexpression = "", returnString = ""; int index1 = 0, index2 = 0; string indexStr = "", newRule = ""; returnString = pRuleString; returnString = (new Regex("\\s+")).Replace(returnString, " "); //Find all instances of the "becomes" keyword and create the edge-detection string foreach (int index in pRuleString.AllIndexesOf("becomes")) { index1 = ExpressionStartIndex(pRuleString, index - 1); index2 = ExpressionEndIndex(pRuleString, index + 7); subexpression = pRuleString.Substring(index1, index2 - index1); //Replace all existing index specifiers with ones decremented by 1 index1 = subexpression.IndexOf('['); newRule = subexpression; while (index1 >= 0) { index2 = subexpression.IndexOf(']', index1); if (index2 > index1) { indexStr = subexpression.Substring(index1 + 1, index2 - index1 - 1); } else { break; } //replace the indexStr with indexes decremented by 1 foreach (Match si in Regex.Matches(indexStr, "[-]*[0-9]+")) { indexStr = indexStr.ReplaceFirstOccurrence(si.Value, (int.Parse(si.Value) - 1).ToString()); } newRule = newRule.ReplaceFirstOccurrence(subexpression.Substring(index1 + 1, index2 - index1 - 1), indexStr); index1 = subexpression.IndexOf('[', index2); } //For variables used without an index specifier (indicating current data), need to add [-1] specifier foreach (Variable vari in RuleParserInputs.VarList) { MatchCollection mc = Regex.Matches(newRule, "\\b" + StringEnum.GetStringValue(vari) + "\\b"); int i = 0, offset = 0; while (i < mc.Count) { index1 = mc[i++].Index + offset; index2 = index1 + StringEnum.GetStringValue(vari).Length; if (newRule[index2] != '[' && newRule[index2] != ']') { newRule = newRule.Insert(index2, "[-1]"); offset += 4; } } } //Create the new subexpression, replacing "becomes" with the same rule using 'previous' indexes newRule = "(" + subexpression.Replace("becomes", "") + " AND NOT(" + newRule.Replace("becomes", "") + "))"; newRule = newRule.Replace(" ", " ").Trim(); returnString = returnString.Replace(subexpression, newRule); returnString = (new Regex("\\s+")).Replace(returnString, " "); } return(returnString); }