#include "sierrachart.h" #include "scstudyfunctions.h" /*==========================================================================*/ struct s_LevelLineConfig { SCDateTime Date; SCString Label; uint32_t Color = 0; uint32_t Width = 0; float Value1 = 0; float Value2 = 0; void Clear() { Date.Clear(); Label.Clear(); Color = 0; Width = 0; Value1 = 0; Value2 = 0; } }; enum TradingLevelsDataLineTypeEnum : int//must be an integer { DataLineTypeDateRepeatingValues = 0 , DataLineTypeDateFormattingValue = 1 , DataLineTypeDateFormattingTopBottomValues = 2 }; const char* HTTP_NO_DATA_RESPONSE = "NO_DATA"; /*==========================================================================*/ int TradingLevelsStudy_RequestValuesFromServer ( SCStudyInterfaceRef sc , const SCString& BaseWebsiteURL , int& r_RequestState , const char* AlternateSymbol ); void TradingLevelsStudy_ResetStateForNextRequest ( SCStudyInterfaceRef sc , int& r_RequestState , SCDateTime& r_RequestDateTime , std::vector* p_ValuesLineForDates , std::vector>* p_PointersToValuesForDates ); unsigned int TradingLevelsStudy_GetValuesForDate ( SCStudyInterfaceRef sc , const SCDateTime& BarDate , float* Values , unsigned int ValuesArraySize , std::vector>* p_PointersToValuesForDates , int& r_LastFoundIndex ); unsigned int TradingLevelsStudy_GetFormattedValuesForDate (SCStudyInterfaceRef sc , const SCDateTime& BarDate , s_LevelLineConfig* FormattedLevelValues , unsigned int ArraySize , std::vector>* p_PointersToValuesForDates , int& r_LastFoundIndex ); void TradingLevelsStudyCore ( SCStudyInterfaceRef sc , const SCString& RequestURL , const int RequestIntervalInMinutes , std::vector* p_ValuesLineForDates , std::vector>* p_PointersToValuesForDates , int& r_RequestState , SCDateTime& r_RequestDateTime , int& r_ClearAtMidnight , bool AllowRequestLevelsFromServer , int& r_AwaitingNextRequestForLevels , const char* InAlternateSymbol , int& r_LastFoundLevelsArrayIndex , int& r_DataLineType ); /*==========================================================================*/ SCSFExport scsf_TradingLevelsStudy(SCStudyInterfaceRef sc) { SCInputRef Input_InVersion = sc.Input[0]; SCInputRef Input_InRequestIntervalInMinutes = sc.Input[1]; SCInputRef Input_InUseTimeRangeForLevelsRequest = sc.Input[2]; SCInputRef Input_InTimeRangeTimeZone = sc.Input[3]; SCInputRef Input_InRequestStartTime = sc.Input[4]; SCInputRef Input_InRequestEndTime = sc.Input[5]; SCInputRef Input_InAlternateSymbol = sc.Input[6]; const SCString RequestURL = "https://www.sierrachart.com/API.php?Service=PriceLevels"; if (sc.SetDefaults) { sc.GraphName = "Trading Levels"; sc.GraphRegion = 0; sc.ScaleRangeType = SCALE_SAMEASREGION; sc.ValueFormat = VALUEFORMAT_INHERITED; sc.AutoLoop = 0; sc.StudyDescription = "Requests line level values from Web server in the format 'YYYY-MM-DD, [level_value_1], [level_value_2], [level_value_3], [level_value_4], and so on, where the level_value_# is equal to the value of the line. There can be up to SC_SUBGRAPHS_AVAILABLE level values."; for (int Index = 0; Index < SC_SUBGRAPHS_AVAILABLE; Index++) { SCString SubgraphName; SubgraphName.Format("Level %d", Index+1); sc.Subgraph[Index].Name = SubgraphName; sc.Subgraph[Index].DrawStyle = DRAWSTYLE_DASH; sc.Subgraph[Index].LineWidth = 2; sc.Subgraph[Index].PrimaryColor = RGB(0, 255, 0); sc.Subgraph[Index].DrawZeros = 0; // false sc.Subgraph[Index].LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_CENTER | LL_VALUE_ALIGN_VALUES_SCALE; } Input_InRequestIntervalInMinutes.Name = "Request Interval in Minutes"; Input_InRequestIntervalInMinutes.SetInt(60); Input_InRequestIntervalInMinutes.SetIntLimits(1, MINUTES_PER_DAY); Input_InUseTimeRangeForLevelsRequest.Name = "Use Time Range For Levels Request"; Input_InUseTimeRangeForLevelsRequest.SetYesNo(false); Input_InTimeRangeTimeZone.Name = "Time Range Time Zone"; Input_InTimeRangeTimeZone.SetTimeZone(TIMEZONE_NEW_YORK); Input_InRequestStartTime.Name = "Request Start Time"; Input_InRequestStartTime.SetTime(0); Input_InRequestEndTime.Name = "Request End Time"; Input_InRequestEndTime.SetTime(SECONDS_PER_DAY - 1); Input_InAlternateSymbol.Name = "Alternate Symbol"; Input_InAlternateSymbol.SetString(""); sc.TextInputName = "Request Identifier"; return; } std::vector* p_ValuesLineForDates = reinterpret_cast*>(sc.GetPersistentPointer(1)); std::vector>* p_PointersToValuesForDates = reinterpret_cast>*>(sc.GetPersistentPointer(2)); int& r_RequestState = sc.GetPersistentInt(1); int& r_ClearAtMidnight = sc.GetPersistentInt(2); int& r_AwaitingNextRequestForLevels = sc.GetPersistentInt(3); int& r_LastFoundLevelsArrayIndex = sc.GetPersistentInt(4); int& r_DataLineType = sc.GetPersistentInt(5); SCDateTime& r_RequestDateTime = sc.GetPersistentSCDateTime(2); SCDateTime CurrentDateTimeInRequestTimeZone; bool AllowRequestLevelsFromServer = true; if (Input_InUseTimeRangeForLevelsRequest.GetYesNo()) { CurrentDateTimeInRequestTimeZone = sc.CurrentSystemDateTime; CurrentDateTimeInRequestTimeZone = sc.ConvertDateTimeFromChartTimeZone(CurrentDateTimeInRequestTimeZone, Input_InTimeRangeTimeZone.GetTimeZone()); if (CurrentDateTimeInRequestTimeZone.GetTimeInSeconds() < Input_InRequestStartTime.GetTime() || CurrentDateTimeInRequestTimeZone.GetTimeInSeconds() > Input_InRequestEndTime.GetTime()) AllowRequestLevelsFromServer = false; } if (sc.IsFullRecalculation) { r_LastFoundLevelsArrayIndex = 0; r_DataLineType = DataLineTypeDateRepeatingValues; } TradingLevelsStudyCore ( sc , RequestURL , Input_InRequestIntervalInMinutes.GetInt() , p_ValuesLineForDates , p_PointersToValuesForDates , r_RequestState , r_RequestDateTime , r_ClearAtMidnight , AllowRequestLevelsFromServer , r_AwaitingNextRequestForLevels , Input_InAlternateSymbol.GetString() , r_LastFoundLevelsArrayIndex , r_DataLineType ); } /*==========================================================================*/ void TradingLevelsStudyCore ( SCStudyInterfaceRef sc , const SCString& RequestURL , const int RequestIntervalInMinutes , std::vector* p_ValuesLineForDates , std::vector>* p_PointersToValuesForDates , int& r_RequestState , SCDateTime& r_RequestDateTime , int& r_ClearAtMidnight , bool AllowRequestLevelsFromServer , int& r_AwaitingNextRequestForLevels , const char* InAlternateSymbol , int& r_LastFoundLevelsArrayIndex , int& r_DataLineType ) { if(sc.LastCallToFunction) { if(p_ValuesLineForDates != NULL) { delete p_ValuesLineForDates; sc.SetPersistentPointer(1, NULL); } if(p_PointersToValuesForDates != NULL) { delete p_PointersToValuesForDates; sc.SetPersistentPointer(2, NULL); } return; } if (p_ValuesLineForDates == NULL) { p_ValuesLineForDates = new std::vector; if(p_ValuesLineForDates != NULL) sc.SetPersistentPointer(1, p_ValuesLineForDates); else { sc.AddMessageToLog("Memory allocation error.", 1); return; } } if (p_PointersToValuesForDates == NULL) { p_PointersToValuesForDates = new std::vector >; if(p_PointersToValuesForDates != NULL) sc.SetPersistentPointer(2, p_PointersToValuesForDates); else { sc.AddMessageToLog("Memory allocation error.", 1); return; } } if (sc.IsFullRecalculation) r_ClearAtMidnight = false; if (AllowRequestLevelsFromServer) { //Request data on a full recalculation and also at the specified interval if (sc.UpdateStartIndex == 0 && r_RequestState == HTTP_REQUEST_RECEIVED) { TradingLevelsStudy_ResetStateForNextRequest(sc, r_RequestState, r_RequestDateTime, p_ValuesLineForDates, p_PointersToValuesForDates); } else if (r_RequestDateTime.IsUnset() || ( (sc.CurrentSystemDateTime - r_RequestDateTime) >= SCDateTime::MINUTES(RequestIntervalInMinutes)) )// Request interval has elapsed { TradingLevelsStudy_ResetStateForNextRequest(sc, r_RequestState, r_RequestDateTime, p_ValuesLineForDates, p_PointersToValuesForDates); } if (TradingLevelsStudy_RequestValuesFromServer(sc, RequestURL, r_RequestState, InAlternateSymbol)) { if (r_RequestState == HTTP_REQUEST_MADE) r_AwaitingNextRequestForLevels = false; return;//Return here since we need to wait for the response } } if (sc.HTTPRequestID != 0)//response received { r_RequestState = HTTP_REQUEST_RECEIVED; if (sc.HTTPResponse == ACSIL_HTTP_REQUEST_ERROR_TEXT || sc.HTTPResponse == ACSIL_HTTP_EMPTY_RESPONSE_TEXT) { sc.AddMessageToLog("There was an error requesting data from the server.", true); } if (sc.UpdateStartIndex == 0 && sc.HTTPResponse == HTTP_NO_DATA_RESPONSE) sc.AddMessageToLog("There are no price levels for the given parameters.", false); if (strstr(sc.HTTPResponse.GetChars(), "CLEAR_AT_MIDNIGHT") != NULL) { sc.AddMessageToLog("Received clear at midnight command.", false); r_ClearAtMidnight = true; } } if (r_RequestState != HTTP_REQUEST_RECEIVED) return; SCDateTime StartIndexDate = sc.BaseDateTimeIn[sc.UpdateStartIndex].GetDate(); SCDateTime LastBarDate = sc.BaseDateTimeIn[sc.ArraySize - 1].GetDate(); if (r_ClearAtMidnight && StartIndexDate != LastBarDate) { //clear existing levels p_ValuesLineForDates->clear(); p_PointersToValuesForDates->clear(); for (int BarIndex = sc.ArraySize - 1; BarIndex >= 0; --BarIndex) { for (unsigned int LevelIndex = 0; LevelIndex < SC_SUBGRAPHS_AVAILABLE; LevelIndex++) { sc.Subgraph[LevelIndex][BarIndex] = 0; } } r_ClearAtMidnight = false;//Prevent this from happening on next calculation. r_AwaitingNextRequestForLevels = true; } if (r_AwaitingNextRequestForLevels) return; if (sc.HTTPResponse == HTTP_NO_DATA_RESPONSE) return; bool FullRecalculate = false; if(p_ValuesLineForDates->empty()) { r_LastFoundLevelsArrayIndex = 0; SCString DataLineTypeDateFormattingValueString("Date, Label, Color, Width, Value"); SCString DataLineTypeDateFormattingTopBottomValuesString("Date, Label, Color, Width, TopValue, BottomValue"); if (sc.HTTPResponse.GetSubString(DataLineTypeDateFormattingValueString.GetLength(), 0) == DataLineTypeDateFormattingValueString) { r_DataLineType = DataLineTypeDateFormattingValue; } else if (sc.HTTPResponse.GetSubString(DataLineTypeDateFormattingTopBottomValuesString.GetLength(), 0) == DataLineTypeDateFormattingTopBottomValuesString) { r_DataLineType = DataLineTypeDateFormattingTopBottomValues; } sc.HTTPResponse.ParseLines(*p_ValuesLineForDates); SCString Message; Message.Format("Received %d price level lines of data from server.", p_ValuesLineForDates->size()); sc.AddMessageToLog(Message, false); std::vector EmptyVector; for (int Index = 0; Index < static_cast(p_ValuesLineForDates->size()); Index++) { p_PointersToValuesForDates->push_back(EmptyVector); p_ValuesLineForDates->at(Index).Tokenize(",", p_PointersToValuesForDates->back()); } FullRecalculate = true; } if(p_PointersToValuesForDates->empty()) return; if (FullRecalculate) sc.UpdateStartIndex = 0; float LevelValues[SC_SUBGRAPHS_AVAILABLE] = {}; s_LevelLineConfig FormattedLevelValues[SC_SUBGRAPHS_AVAILABLE]; SCDateTime PriorDate; uint32_t MaximumLevelsUsedPerDay = 0; uint32_t NumberUsedLevels = 0; for (int BarIndex = sc.UpdateStartIndex; BarIndex < sc.ArraySize; BarIndex++) { SCDateTime BarIndexDate = sc.GetTradingDayDate(sc.BaseDateTimeIn[BarIndex]); if (r_DataLineType == DataLineTypeDateRepeatingValues) { if (PriorDate != BarIndexDate) { NumberUsedLevels = TradingLevelsStudy_GetValuesForDate(sc, BarIndexDate, LevelValues, SC_SUBGRAPHS_AVAILABLE, p_PointersToValuesForDates, r_LastFoundLevelsArrayIndex); if (NumberUsedLevels > MaximumLevelsUsedPerDay) MaximumLevelsUsedPerDay = NumberUsedLevels; PriorDate = BarIndexDate; } for (uint32_t LevelIndex = 0; LevelIndex < NumberUsedLevels; LevelIndex++) sc.Subgraph[LevelIndex][BarIndex] = LevelValues[LevelIndex]; } else if (r_DataLineType == DataLineTypeDateFormattingValue) { if (PriorDate != BarIndexDate) { NumberUsedLevels = TradingLevelsStudy_GetFormattedValuesForDate(sc, BarIndexDate, FormattedLevelValues, SC_SUBGRAPHS_AVAILABLE, p_PointersToValuesForDates, r_LastFoundLevelsArrayIndex); for (uint32_t LevelIndex = 0; LevelIndex < NumberUsedLevels; LevelIndex++) { sc.Subgraph[LevelIndex].Name = FormattedLevelValues[LevelIndex].Label; sc.Subgraph[LevelIndex].LineWidth = FormattedLevelValues[LevelIndex].Width; sc.Subgraph[LevelIndex].PrimaryColor = FormattedLevelValues[LevelIndex].Color; } if (NumberUsedLevels > MaximumLevelsUsedPerDay) MaximumLevelsUsedPerDay = NumberUsedLevels; PriorDate = BarIndexDate; } for (uint32_t LevelIndex = 0; LevelIndex < NumberUsedLevels; LevelIndex++) sc.Subgraph[LevelIndex][BarIndex] = FormattedLevelValues[LevelIndex].Value1; } else if (r_DataLineType == DataLineTypeDateFormattingTopBottomValues) { if (PriorDate != BarIndexDate) { NumberUsedLevels = TradingLevelsStudy_GetFormattedValuesForDate(sc, BarIndexDate, FormattedLevelValues, SC_SUBGRAPHS_AVAILABLE, p_PointersToValuesForDates, r_LastFoundLevelsArrayIndex); if (NumberUsedLevels > (SC_SUBGRAPHS_AVAILABLE / 2)) NumberUsedLevels = (SC_SUBGRAPHS_AVAILABLE / 2); for (uint32_t LevelIndex = 0; LevelIndex < NumberUsedLevels; LevelIndex++) { int SubgraphIndex = LevelIndex * 2; sc.Subgraph[SubgraphIndex].Name = FormattedLevelValues[LevelIndex].Label; sc.Subgraph[SubgraphIndex].Name += " Top"; sc.Subgraph[SubgraphIndex].LineWidth = FormattedLevelValues[LevelIndex].Width; sc.Subgraph[SubgraphIndex].PrimaryColor = FormattedLevelValues[LevelIndex].Color; uint16_t& r_DrawStyleTop = sc.Subgraph[SubgraphIndex].DrawStyle; uint16_t& r_DrawStyleBottom = sc.Subgraph[SubgraphIndex + 1].DrawStyle; if (r_DrawStyleTop != DRAWSTYLE_FILL_RECTANGLE_TOP && r_DrawStyleTop != DRAWSTYLE_TRANSPARENT_FILL_RECTANGLE_TOP) { r_DrawStyleTop = DRAWSTYLE_TRANSPARENT_FILL_RECTANGLE_TOP; r_DrawStyleBottom = DRAWSTYLE_TRANSPARENT_FILL_RECTANGLE_BOTTOM; } if (r_DrawStyleTop == DRAWSTYLE_FILL_RECTANGLE_TOP) r_DrawStyleBottom = DRAWSTYLE_FILL_RECTANGLE_BOTTOM; else if (r_DrawStyleTop == DRAWSTYLE_TRANSPARENT_FILL_RECTANGLE_TOP) r_DrawStyleBottom = DRAWSTYLE_TRANSPARENT_FILL_RECTANGLE_BOTTOM; sc.Subgraph[SubgraphIndex + 1].Name = FormattedLevelValues[LevelIndex].Label; sc.Subgraph[SubgraphIndex + 1].Name += " Bottom"; sc.Subgraph[SubgraphIndex + 1].LineWidth = FormattedLevelValues[LevelIndex].Width; sc.Subgraph[SubgraphIndex + 1].PrimaryColor = FormattedLevelValues[LevelIndex].Color; } //if (NumberUsedLevels > MaximumLevelsUsedPerDay) // MaximumLevelsUsedPerDay = NumberUsedLevels; PriorDate = BarIndexDate; } for (uint32_t LevelIndex = 0; LevelIndex < NumberUsedLevels; LevelIndex++) { int SubgraphIndex = LevelIndex * 2; sc.Subgraph[SubgraphIndex][BarIndex] = FormattedLevelValues[LevelIndex].Value1; sc.Subgraph[SubgraphIndex + 1][BarIndex] = FormattedLevelValues[LevelIndex].Value2; } } } if (FullRecalculate && r_DataLineType != DataLineTypeDateFormattingTopBottomValues) { for (uint32_t Index = 0; Index < SC_SUBGRAPHS_AVAILABLE; Index++) { if (Index < MaximumLevelsUsedPerDay && !sc.IsVisibleSubgraphDrawStyle(sc.Subgraph[Index].DrawStyle)) { sc.Subgraph[Index].DrawStyle = DRAWSTYLE_DASH; sc.Subgraph[Index].DisplayNameValueInDataLine = true; sc.Subgraph[Index].DisplayNameValueInWindowsFlags = true; } else if (Index >= MaximumLevelsUsedPerDay) { sc.Subgraph[Index].DrawStyle = DRAWSTYLE_IGNORE; sc.Subgraph[Index].DisplayNameValueInDataLine = false; sc.Subgraph[Index]. DisplayNameValueInWindowsFlags = false; } } } } /*==========================================================================*/ //Returns 1 if request has been made. Returns 0 if request has not been made. int TradingLevelsStudy_RequestValuesFromServer ( SCStudyInterfaceRef sc , const SCString& BaseWebsiteURL , int& r_RequestState , const char* AlternateSymbol ) { SCString FullURL; if (r_RequestState != HTTP_REQUEST_NOT_SENT) return 0; if (sc.TextInput.IsEmpty()) return 0; const char* RequestSymbol = NULL; if (AlternateSymbol != NULL && AlternateSymbol[0] != '\0') RequestSymbol = AlternateSymbol; else RequestSymbol = sc.Symbol.GetChars(); FullURL.Format ("%s&Username=%s&Symbol=%s&SCDLLName=%s" , BaseWebsiteURL.GetChars() , sc.UserName().GetChars() , RequestSymbol , sc.TextInput.GetChars() ); if (!sc.MakeHTTPRequest(FullURL)) { sc.AddMessageToLog("Error making HTTP Request.", true); r_RequestState = HTTP_REQUEST_ERROR; } else { r_RequestState = HTTP_REQUEST_MADE; SCString LogMessage("Requesting data from Trading Levels server: "); LogMessage += FullURL; sc.AddMessageToLog(LogMessage, false); } return 1; } /*==========================================================================*/ void TradingLevelsStudy_ResetStateForNextRequest ( SCStudyInterfaceRef sc , int& r_RequestState , SCDateTime& r_RequestDateTime , std::vector* p_ValuesLineForDates , std::vector>* p_PointersToValuesForDates ) { p_ValuesLineForDates->clear(); p_PointersToValuesForDates->clear(); r_RequestState = HTTP_REQUEST_NOT_SENT; r_RequestDateTime = sc.CurrentSystemDateTime; } /*==========================================================================*/ unsigned int TradingLevelsStudy_GetValuesForDate ( SCStudyInterfaceRef sc , const SCDateTime& BarDate , float* Values , unsigned int ValuesArraySize , std::vector>* p_PointersToValuesForDates , int& r_LastFoundIndex ) { //Clean array memset(Values, 0, sizeof(float) * ValuesArraySize); if (p_PointersToValuesForDates->empty()) return 0; unsigned int NumberLevels = 0; const int EndIndex = static_cast(p_PointersToValuesForDates->size()); for(; r_LastFoundIndex < EndIndex; r_LastFoundIndex++) { std::vector& Fields = p_PointersToValuesForDates->at(r_LastFoundIndex); if (Fields.empty()) continue; SCDateTime SourceDataLineDate = sc.DateStringToSCDateTime(Fields[0]); if (SourceDataLineDate < BarDate) continue; if (SourceDataLineDate > BarDate) return NumberLevels;//r_LastFoundIndex will be used on the next entry and continue from the correct index. int FieldsSize = static_cast(Fields.size()); for (int ItemIndex = 1; ItemIndex < FieldsSize; ItemIndex++) { Values[NumberLevels] = static_cast(sc.StringToDouble(Fields[ItemIndex])); NumberLevels++; if (NumberLevels >= ValuesArraySize) break; } break; } return NumberLevels; } /*==========================================================================*/ unsigned int TradingLevelsStudy_GetFormattedValuesForDate (SCStudyInterfaceRef sc , const SCDateTime& BarDate , s_LevelLineConfig* FormattedLevelValues , unsigned int ArraySize , std::vector>* p_PointersToValuesForDates , int& r_LastFoundIndex ) { //Clean array for (uint32_t Index = 0; Index < ArraySize; Index++) { FormattedLevelValues[Index].Clear(); } if (p_PointersToValuesForDates->empty()) return 0; unsigned int LevelIndex = 0; const int EndIndex = static_cast(p_PointersToValuesForDates->size()); for (; r_LastFoundIndex < EndIndex; r_LastFoundIndex++) { std::vector& Fields = p_PointersToValuesForDates->at(r_LastFoundIndex); if (Fields.empty()) continue; //"Date, Label, Color, Width, Value" //"Date, Label, Color, Width, TopValue, BottomValue" SCDateTime SourceDataLineDate = sc.DateStringToSCDateTime(Fields[0]); if (SourceDataLineDate < BarDate) continue; if (SourceDataLineDate > BarDate) return LevelIndex;//r_LastFoundIndex will be used on the next entry and continue from the correct index. if (Fields.size() < 6) continue; FormattedLevelValues[LevelIndex].Date = SourceDataLineDate; FormattedLevelValues[LevelIndex].Label = Fields[1]; FormattedLevelValues[LevelIndex].Color = atoi(Fields[2]); const int ColorValue = FormattedLevelValues[LevelIndex].Color; uint8_t Red = (ColorValue & 0xff0000) >> 16; uint8_t Green = (ColorValue & 0xff00) >> 8; uint8_t Blue = ColorValue & 0xff; FormattedLevelValues[LevelIndex].Color = RGB(Red, Green, Blue); FormattedLevelValues[LevelIndex].Width = atoi(Fields[3]); FormattedLevelValues[LevelIndex].Value1 = static_cast(atof(Fields[4])); if(Fields.size() >= 6) FormattedLevelValues[LevelIndex].Value2 = static_cast(atof(Fields[5])); LevelIndex++; if (LevelIndex >= ArraySize) break; } return LevelIndex;//Num levels }