TA_RetCode TA_DictAddPair_S2( TA_Dict *dict, TA_String *key1, TA_String *key2, void *value ) { TA_PrivDictInfo *theDict; dnode_t *node; TA_String *dupKey; TA_Dict *subDict; TA_Libc *libHandle; dict_t *kazlibDict; theDict = (TA_PrivDictInfo *)dict; if( (theDict == NULL) || (key1 == NULL) || (key2 == NULL) || (value == NULL) ) return TA_BAD_PARAM; kazlibDict = &theDict->d; libHandle = theDict->libHandle; /* Verify if a a dictionary already exist for key1. */ node = dict_lookup( libHandle, kazlibDict, TA_StringToChar(key1) ); if( node ) { /* A dictionary already exist with the same key1... re-use it. */ subDict = (TA_Dict *)dnode_get( node ); } else { /* Alloc a new directory corresponding to key1. */ subDict = TA_DictAlloc( libHandle, TA_DICT_KEY_ONE_STRING, theDict->freeValueFunc ); if( !subDict ) return TA_ALLOC_ERR; dupKey = TA_StringDup( TA_GetGlobalStringCache( libHandle ), key1 ); if( !dupKey ) { TA_DictFree( subDict ); return TA_ALLOC_ERR; } if( !dict_alloc_insert( libHandle, kazlibDict, TA_StringToChar(dupKey), subDict ) ) { TA_DictFree( subDict ); TA_StringFree( TA_GetGlobalStringCache( libHandle ), dupKey ); return TA_ALLOC_ERR; } } /* Insert the string in the subDict using key2 */ return TA_DictAddPair_S( subDict, key2, value ); }
TA_RetCode TA_DictAddPair_S( TA_Dict *dict, TA_String *key, void *value ) { TA_PrivDictInfo *theDict; dnode_t *node; TA_String *dupKey; TA_Libc *libHandle; dict_t *kazlibDict; theDict = (TA_PrivDictInfo *)dict; if( (theDict == NULL) || (key == NULL) || (value == NULL) ) return TA_BAD_PARAM; kazlibDict = &theDict->d; libHandle = theDict->libHandle; /* Verify if an entry exist already with the same key. */ node = dict_lookup( libHandle, kazlibDict, TA_StringToChar(key) ); if( node ) { /* An entry already exist with the same key... * Re-use the existing node. Just replace the * 'value' part. * De-allocate the older 'value'. */ if( theDict->freeValueFunc ) (*theDict->freeValueFunc)( libHandle, dnode_get( node ) ); dnode_put( node, value ); } else { /* Alloc/insert a new key-value pair in the dictionary. */ dupKey = TA_StringDup( TA_GetGlobalStringCache( libHandle ), key ); if( !dupKey ) return TA_ALLOC_ERR; if( !dict_alloc_insert( libHandle, kazlibDict, TA_StringToChar(dupKey), value ) ) { TA_StringFree( TA_GetGlobalStringCache( libHandle ), dupKey ); return TA_ALLOC_ERR; } } return TA_SUCCESS; }
/**** Local functions definitions. ****/ static TA_RetCode doAdjustments( TA_DecodingParam *localDecodingParam, TA_String *yahooName, TA_PrivateYahooHandle *yahooHandle, const char *overideServerAddr, TA_ParamForAddData *paramForAddData ) { TA_RetCode retCode; TA_WebPage *webPage; TA_Stream *stream; TA_StreamAccess *streamAccess; TA_AdjustDataParse adjustDataParse; adjustDataParse.paramForAddData = paramForAddData; adjustDataParse.yahooHandle = yahooHandle; /* Get the split/value adjustments from the Yahoo! web site */ retCode = TA_WebPageAllocFromYahooName( localDecodingParam, TA_StringToChar(yahooName), overideServerAddr, &webPage, paramForAddData ); if( retCode != TA_SUCCESS ) return retCode; /* Parse the dividend/split information */ stream = webPage->content; streamAccess = TA_StreamAccessAlloc( stream ); if( !streamAccess ) { TA_WebPageFree( webPage ); return TA_ALLOC_ERR; } retCode = TA_StreamAccessSearch( streamAccess, "Monthly" ); if( retCode == TA_SUCCESS ) { retCode = TA_StreamAccessSearch( streamAccess, "Last" ); if( retCode == TA_SUCCESS ) { /* Find the table of data. */ retCode = TA_StreamAccessSearch( streamAccess, "yfnc_datamodoutline1" );; if( retCode == TA_SUCCESS ) { retCode = TA_StreamAccessGetHTMLTable( streamAccess, 200, processAdjustmentTable, &adjustDataParse ); } } } TA_StreamAccessFree( streamAccess ); TA_WebPageFree( webPage ); return retCode; }
TA_RetCode TA_YAHOO_OpenSource( const TA_AddDataSourceParamPriv *param, TA_DataSourceHandle **handle ) { TA_PROLOG TA_DataSourceHandle *tmpHandle; TA_PrivateYahooHandle *privData; TA_RetCode retCode; TA_StringCache *stringCache; TA_CountryId countryId; TA_CountryId countryIdTemp; TA_Timestamp now; const char *locationPtr; char locationBuffer[3]; int timeout_set; /* boolean */ int i, again; unsigned int strLength, strServerLength; const char *strTemp; *handle = NULL; TA_TRACE_BEGIN( TA_YAHOO_OpenSource ); stringCache = TA_GetGlobalStringCache(); /* Verify that the requested functionality is supported or not. */ if( param->flags & TA_REPLACE_ZERO_PRICE_BAR ) { TA_TRACE_RETURN( TA_NOT_SUPPORTED ); } /* Allocate and initialize the handle. This function will also allocate the * private handle (opaque data). */ tmpHandle = TA_YAHOO_DataSourceHandleAlloc(); if( tmpHandle == NULL ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } privData = (TA_PrivateYahooHandle *)(tmpHandle->opaqueData); /* Copy some parameters in the private handle. */ privData->param = param; /* Indentify the country and replace with the default as needed. * At the same time, identify optional "server=" modifier. */ countryId = TA_Country_ID_INVALID; if( privData->param->location ) { locationPtr = TA_StringToChar(privData->param->location); /* Split into token (seperator is ';'). Check for a 2 character country * string and the optional "server=" modifier. */ i = -1; strTemp = &locationPtr[0]; strLength = 0; strServerLength = strlen(TA_SERVER_STR); again = 1; while( (++i < 1024) && again ) { if( strLength == 0 ) strTemp = &locationPtr[i]; if( locationPtr[i] == '\0' ) again = 0; if( locationPtr[i] == ';' || !again ) { if( strLength == 2 ) { locationBuffer[0] = locationPtr[i-2]; locationBuffer[1] = locationPtr[i-1]; locationBuffer[2] = '\0'; countryIdTemp = TA_CountryAbbrevToId(locationBuffer); if( countryIdTemp == TA_Country_ID_INVALID ) TA_TRACE_RETURN( TA_UNSUPPORTED_COUNTRY ); if( countryId != TA_Country_ID_INVALID ) TA_TRACE_RETURN( TA_LIMIT_OF_ONE_COUNTRY_ID_EXCEEDED ); countryId = countryIdTemp; } else if( strLength>strServerLength && (strncmp(strTemp,TA_SERVER_STR,strServerLength)==0) ) { if( privData->userSpecifiedServer ) TA_TRACE_RETURN( TA_LIMIT_OF_ONE_SERVER_EXCEEDED ); privData->userSpecifiedServer = TA_StringAllocN( stringCache, &strTemp[strServerLength], strLength-strServerLength ); TA_ASSERT( privData->userSpecifiedServer != NULL ); } else TA_TRACE_RETURN( TA_LOCATION_PARAM_INVALID ); strLength = 0; } else strLength++; } } if( countryId == TA_Country_ID_INVALID ) { /* Default is United States. */ countryId = TA_Country_ID_US; } if( privData->param->id == TA_YAHOO_ONE_SYMBOL ) { privData->index = NULL; privData->webSiteCountry = countryId; privData->webSiteSymbol = TA_StringDup(stringCache,privData->param->info); tmpHandle->nbCategory = 1; } else { /* Build the index using .dat files */ switch( countryId ) { case TA_Country_ID_US: /* United States */ case TA_Country_ID_CA: /* Canada */ case TA_Country_ID_UK: /* United Kingdom */ case TA_Country_ID_DE: /* Germany */ case TA_Country_ID_DK: /* Denmark */ case TA_Country_ID_ES: /* Spain */ case TA_Country_ID_FR: /* France */ case TA_Country_ID_IT: /* Italy */ case TA_Country_ID_SE: /* Sweden */ case TA_Country_ID_NO: /* Norway */ /* These country are currently supported. */ break; default: TA_YAHOO_DataSourceHandleFree( tmpHandle ); TA_TRACE_RETURN( TA_UNSUPPORTED_COUNTRY ); } /* Establish the timeout for local cache of the index. * Let's make it 4 business days. */ timeout_set = 0; TA_SetDefault( &now ); retCode = TA_SetDateNow( &now ); for( i=0; (i < 4) && (retCode == TA_SUCCESS); i++ ) retCode = TA_PrevWeekday( &now ); if( (i == 4) && (retCode == TA_SUCCESS) ) timeout_set = 1; /* At this point, we got all the information we * need in the handle. * Now build the TA_YahooIdx. */ retCode = TA_YahooIdxAlloc( countryId, &privData->index, TA_USE_LOCAL_CACHE|TA_USE_REMOTE_CACHE, NULL, timeout_set?&now:NULL, NULL ); if( retCode != TA_SUCCESS ) { TA_YAHOO_DataSourceHandleFree( tmpHandle ); TA_TRACE_RETURN( retCode ); } /* Set the total number of distinct category. */ tmpHandle->nbCategory = privData->index->nbCategory; } *handle = tmpHandle; TA_TRACE_RETURN( TA_SUCCESS ); }
TA_RetCode TA_YAHOO_OpenSource( const TA_AddDataSourceParamPriv *param, TA_DataSourceHandle **handle ) { TA_PROLOG TA_DataSourceHandle *tmpHandle; TA_PrivateYahooHandle *privData; TA_RetCode retCode; TA_StringCache *stringCache; TA_CountryId countryId; TA_Timestamp now; const char *locationPtr; int timeout_set; /* boolean */ int i; *handle = NULL; TA_TRACE_BEGIN( TA_YAHOO_OpenSource ); stringCache = TA_GetGlobalStringCache(); /* Verify that the requested functionality is supported or not. */ if( param->flags & TA_REPLACE_ZERO_PRICE_BAR ) { TA_TRACE_RETURN( TA_NOT_SUPPORTED ); } /* Allocate and initialize the handle. This function will also allocate the * private handle (opaque data). */ tmpHandle = TA_YAHOO_DataSourceHandleAlloc(); if( tmpHandle == NULL ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } privData = (TA_PrivateYahooHandle *)(tmpHandle->opaqueData); /* Copy some parameters in the private handle. */ privData->param = param; /* Validate if this is a supported country. */ if( !privData->param->location ) { countryId = TA_Country_ID_US; } else { locationPtr = TA_StringToChar(privData->param->location); countryId = TA_CountryAbbrevToId(locationPtr); switch( countryId ) { case TA_Country_ID_US: /* United States */ case TA_Country_ID_CA: /* Canada */ case TA_Country_ID_UK: /* United Kingdom */ case TA_Country_ID_DE: /* Germany */ case TA_Country_ID_DK: /* Denmark */ case TA_Country_ID_ES: /* Spain */ case TA_Country_ID_FR: /* France */ case TA_Country_ID_IT: /* Italy */ case TA_Country_ID_SE: /* Sweden */ case TA_Country_ID_NO: /* Norway */ /* These country are currently supported. */ break; default: TA_YAHOO_DataSourceHandleFree( tmpHandle ); TA_TRACE_RETURN( TA_UNSUPPORTED_COUNTRY ); } } /* Establish the timeout for local cache of the index. * Let's make it 3 business days. */ timeout_set = 0; TA_SetDefault( &now ); retCode = TA_SetDateNow( &now ); for( i=0; (i < 3) && (retCode == TA_SUCCESS); i++ ) retCode = TA_PrevWeekday( &now ); if( (i == 3) && (retCode == TA_SUCCESS) ) timeout_set = 1; /* At this point, we got all the information we * need in the handle. * Now build the TA_YahooIdx. */ retCode = TA_YahooIdxAlloc( countryId, &privData->index, TA_USE_LOCAL_CACHE|TA_USE_REMOTE_CACHE, NULL, timeout_set?&now:NULL, NULL ); if( retCode != TA_SUCCESS ) { TA_YAHOO_DataSourceHandleFree( tmpHandle ); TA_TRACE_RETURN( retCode ); } /* Set the total number of distinct category. */ tmpHandle->nbCategory = privData->index->nbCategory; *handle = tmpHandle; TA_TRACE_RETURN( TA_SUCCESS ); }
/**** Global functions definitions. ****/ TA_RetCode TA_GetHistoryDataFromWeb( TA_Libc *libHandle, TA_DataSourceHandle *handle, TA_CategoryHandle *categoryHandle, TA_SymbolHandle *symbolHandle, TA_Period period, const TA_Timestamp *start, const TA_Timestamp *end, TA_Field fieldToAlloc, TA_ParamForAddData *paramForAddData ) { TA_PROLOG; TA_RetCode retCode; TA_StringCache *stringCache; TA_String *yahooName; TA_WebPage *webPage; TA_PrivateYahooHandle *yahooHandle; TA_DecodingParam localDecodingParam; const TA_DecodingParam *decodingParam; TA_FileHandle *fileHandle; TA_ReadOpInfo *readOpInfo; UIRSuffixParsing suffixParsing; TA_Timestamp firstBarTimestamp, lastBarTimestamp, prevEndDate; TA_InfoFromAddedData infoFromAddedData; TA_DayOfWeek dayOfWeek; int nbEstimateBar; int nbField; unsigned int nbBarAdded, nbTotalBarAdded; int again, firstTime, nbBatch; int zeroBarAddedAttempt; TA_TRACE_BEGIN( libHandle, TA_GetHistoryDataFromWeb ); /* Initialize some local variables. */ stringCache = TA_GetGlobalStringCache( libHandle ); yahooHandle = (TA_PrivateYahooHandle *)handle->opaqueData; readOpInfo = NULL; nbEstimateBar = 0; TA_ASSERT( libHandle, categoryHandle != NULL ); TA_ASSERT( libHandle, symbolHandle != NULL ); TA_ASSERT( libHandle, categoryHandle->string != NULL ); TA_ASSERT( libHandle, symbolHandle->string != NULL ); /* Set the initial first/last timestamp */ if( start ) TA_TimestampCopy( &firstBarTimestamp, start ); else { TA_SetDate( 1950, 1, 1, &firstBarTimestamp ); TA_SetTime( 0, 0, 0, &firstBarTimestamp ); } if( end ) TA_TimestampCopy( &lastBarTimestamp, end ); else { TA_SetDateNow( &lastBarTimestamp ); TA_SetTime( 0, 0, 0, &lastBarTimestamp ); } /* Make sure that lastBarTimestamp is a week-day. */ dayOfWeek = TA_GetDayOfTheWeek( &lastBarTimestamp ); if( (dayOfWeek == TA_SUNDAY) || (dayOfWeek == TA_SATURDAY) ) TA_PrevWeekday( &lastBarTimestamp ); /* Map the TA-Lib name into the Yahoo! name. */ retCode = TA_AllocStringFromLibName( libHandle, categoryHandle->string, symbolHandle->string, &yahooName ); if( retCode != TA_SUCCESS ) { TA_TRACE_RETURN( retCode ); } TA_ASSERT( libHandle, yahooName != NULL ); TA_ASSERT( libHandle, yahooHandle != NULL ); /* Get the decoding parameter for the CSV web page. */ decodingParam = TA_YahooIdxDecodingParam( yahooHandle->index, TA_YAHOOIDX_CVS_PAGE ); if( !decodingParam ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(103) ); } /* Use a local copy of the decoding param. * This is because the uirSuffix is replaced with * an allocated buffer (so the date field can be * manipulated). */ localDecodingParam = *decodingParam; /* Parse the uirSuffix so the start/end date can be changed. */ if( !setUIRSuffixParsing( decodingParam->uirSuffix, &suffixParsing ) ) { /* This should never happen unless the * Yahoo! index protocol has been broken. */ /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(104) ); } /* Replace the uirSuffix with a large local buffer. */ localDecodingParam.uirSuffix = TA_Malloc( libHandle, suffixParsing.maxTotalLength ); if( !localDecodingParam.uirSuffix ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Change the dates in the uirSuffix. */ buildUIRSuffix( &suffixParsing, &firstBarTimestamp, &lastBarTimestamp, (char *)localDecodingParam.uirSuffix ); /* nbBatch is a safety net to make sure that * TA-Lib won't stay forever in the while loop * in case Yahoo! changes their protocol. */ nbBatch = 0; /* Sometime Yahoo! return an empty csv file. Make * multiple attempts in that case. */ zeroBarAddedAttempt = 0; again = 1; firstTime = 1; nbTotalBarAdded = 0; while( again && (++nbBatch < 100) && (zeroBarAddedAttempt < 10) ) { if( TA_TimestampLess(&lastBarTimestamp,&firstBarTimestamp) ) { /* Get out of this loop if all the requested data * has been retreived already. */ again = 0; break; } retCode = TA_WebPageAllocFromYahooName( libHandle, &localDecodingParam, TA_StringToChar(yahooName), &webPage ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } /* Disguise the webPage stream into a "file". That way the speed * optimized ASCII decoder can be re-used (TA_ReadOp stuff). */ retCode = TA_FileSeqOpenFromStream( libHandle, webPage->content, &fileHandle ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } if( firstTime ) { /* Make assumption of the data provided * base on the number of fields in the CSV file. */ nbField = nbCommaField( webPage->content ); switch( nbField ) { case 2: readOpInfo = yahooHandle->readOp2Fields; break; case 5: readOpInfo = yahooHandle->readOp5Fields; break; default: readOpInfo = yahooHandle->readOp6Fields; } /* User asking for all the fields? */ if( fieldToAlloc == TA_ALL ) { switch( nbField ) { case 2: fieldToAlloc = TA_CLOSE|TA_TIMESTAMP; break; case 5: fieldToAlloc = TA_OPEN|TA_HIGH|TA_LOW|TA_CLOSE|TA_TIMESTAMP; break; default: fieldToAlloc = TA_OPEN|TA_HIGH|TA_LOW|TA_CLOSE|TA_VOLUME|TA_TIMESTAMP; } } /* Optimize the read op for the requested data. */ retCode = TA_ReadOp_Optimize( libHandle, readOpInfo, period, fieldToAlloc ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } /* Make an estimation of the number of price bar. */ nbEstimateBar = TA_StreamCountChar( webPage->content, '\n' ) + 1; if( nbEstimateBar < 100 ) nbEstimateBar = 100; } /* Interpret the CSV data. */ retCode = TA_ReadOp_Do( libHandle, fileHandle, readOpInfo, period, &firstBarTimestamp, &lastBarTimestamp, nbEstimateBar, fieldToAlloc, paramForAddData, &nbBarAdded ); TA_FileSeqClose( libHandle, fileHandle ); TA_WebPageFree( webPage ); nbTotalBarAdded += nbBarAdded; if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } /* Yahoo! does not always return all the data it could, up to * the requested end date. It is important to detect these occurence * and cancel the usage of all data accumulated up to now. */ TA_GetInfoFromAddedData( paramForAddData, &infoFromAddedData ); if( infoFromAddedData.barAddedSinceLastCall ) { /* Do some more checking by considering holidays, week-end etc... */ if( !isGapAcceptable(&infoFromAddedData.highestTimestampAddedSinceLastCall, &lastBarTimestamp) ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( TA_DATA_GAP ); } TA_TimestampCopy( &lastBarTimestamp, &infoFromAddedData.lowestTimestamp ); } #if DEBUG_PRINTF printf( "NB BAR ADDED=%d, TOTAL=%d\n", nbBarAdded, nbTotalBarAdded ); #endif /* Verify if more data should be processed. * Yahoo! sometimes slice their data, in * batch of 200 price bars. */ if( firstTime && (nbBarAdded > 200) ) { again = 0; /* Assume all the data extracted... exit the loop. */ } else if( nbBarAdded == 0 ) { /* Make multiple attempts when retreiving data succeed, * but somehow there is zero bar returned. * * Sometimes this might be correct when there is truly no * more data available, so choosing an algorithm before * giving up is a comprimise between reliability and * usability. The data source is free... and you get * what you pay for after all ;) */ if( (nbTotalBarAdded < 1000) && (zeroBarAddedAttempt >= 1) && (zeroBarAddedAttempt < 7) ) { /* I did choose to add a delay when insufficient total data is returned. When * there is already ~5 years of data, most likely there is "Zero" returned * because there is NO more data available, so just do the retry without delay. */ TA_Sleep(zeroBarAddedAttempt*2); } #if DEBUG_PRINTF printf( "Retry %d", zeroBarAddedAttempt ); #endif zeroBarAddedAttempt++; } else { zeroBarAddedAttempt = 0; if( TA_TimestampEqual( &lastBarTimestamp, &prevEndDate ) ) { /* prevEndDate is a "safety net" to * exit the loop early in case Yahoo! starts * to return always the same batch of data. * Just ignore the repetitive data and exit. */ TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_SUCCESS ); } TA_TimestampCopy( &prevEndDate, &lastBarTimestamp ); /* Request the data up to the day BEFORE * the last batch of data received. */ TA_PrevDay( &lastBarTimestamp ); /* Make sure that lastBarTimestamp is a week-day. */ dayOfWeek = TA_GetDayOfTheWeek( &lastBarTimestamp ); if( (dayOfWeek == TA_SUNDAY) || (dayOfWeek == TA_SATURDAY) ) TA_PrevWeekday( &lastBarTimestamp ); /* Change the dates in the uirSuffix. */ buildUIRSuffix( &suffixParsing, &firstBarTimestamp, &lastBarTimestamp, (char *)localDecodingParam.uirSuffix ); /* From that point, data is expected to be most likely * sent in batch of 200. */ nbEstimateBar = 200; } firstTime = 0; } /* Clean-up and exit */ TA_Free( libHandle, (char *)localDecodingParam.uirSuffix ); TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); }
/**** Global functions definitions. ****/ TA_RetCode TA_FileIndexParsePath( TA_FileIndexPriv *fileIndexPriv, TA_String *path ) { typedef enum { INIT_PROCESSING, FIX_PROCESSING, FIELD_PROCESSING, WILD_PROCESSING, SEP_PROCESSING } State; TA_PROLOG State currentState; const char *currentTokenStart; unsigned int length; char *str; char *pos; char sepTmp[2]; TA_RetCode retCode; unsigned int tokenSize; TA_TokenId tokenId; const char *sourcePattern; TA_TRACE_BEGIN( TA_FileIndexParsePath ); TA_ASSERT( path != NULL ); sepTmp[1] = '\0'; sourcePattern = TA_StringToChar( path ); /* The following macro should help for the readability of the parsing logic. * These macro are used only inside this function. */ #define RETURN(y) {TA_Free(str); TA_TRACE_RETURN( y );} #define REJECT_STATE(x,y) { if(currentState==x)RETURN(y); } #define CHANGE_STATE(x) {currentState=x; currentTokenStart=pos+1;} #define ADD_TOKEN(id,value) \ { \ retCode = addToken(fileIndexPriv,id,value); \ if( retCode != TA_SUCCESS) RETURN(retCode); \ } #define FLUSH_FIX() \ { \ retCode = flushFixPart(fileIndexPriv,currentTokenStart,pos); \ if( retCode != TA_SUCCESS ) RETURN(retCode); \ } /* This function build a list representing the tokens * of the sourcePattern. * * Example: "C:\a\*AZD?\[S]\data.txt" becomes * * TokenId Value * TA_TOK_FIX "C:" * TA_TOK_SEP "\" * TA_TOK_FIX "a" * TA_TOK_SEP "\" * TA_TOK_WILD "*" * TA_TOK_FIX "AZD" * TA_TOK_WILD_CHAR "?" * TA_TOK_SEP "\" * TA_TOK_S "?*" * TA_TOK_SEP "\" * TA_TOK_FIX "data.txt" * TA_TOK_END (null) * * In the values, the '?' and '*' character represent MS-DOS kind * of wildcards: * '?' is any character (but only one). * '*' zero or more of any character */ if( sourcePattern == NULL ) return TA_INVALID_PATH; length = strlen( sourcePattern ) + 1; if( (length <= 1) || (length > 2048) ) return TA_INVALID_PATH; str = (char *)TA_Malloc( length ); strcpy( str, sourcePattern ); pos = str; currentState = INIT_PROCESSING; currentTokenStart = pos; while( *pos != '\0' ) { if( (*pos == '\\') || (*pos == '/') ) { /* Handle directories separator character. */ REJECT_STATE( FIELD_PROCESSING, TA_INVALID_FIELD ); REJECT_STATE( SEP_PROCESSING, TA_INVALID_PATH ); FLUSH_FIX(); #if 0 !!! Needed? /* Check that the string prior to the separator * does not terminate with a dot '.' */ if( currentState != INIT_PROCESSING ) { if( *(pos-1) == '.' ) RETURN( TA_INVALID_PATH ); } #endif /* Transform into the directory delimiter * used on the host file system. */ sepTmp[0] = (char)TA_SeparatorASCII(); ADD_TOKEN( TA_TOK_SEP, sepTmp ); CHANGE_STATE( SEP_PROCESSING ); } else switch( *pos )
static void internalCheckpoint( TA_TraceGlobal *global, TA_String *key, const char *funcname, const char *filename, unsigned int lineNb ) { #if !defined( TA_SINGLE_THREAD ) TA_RetCode retCode; #endif #ifdef TA_DEBUG TA_TracePosition *tracePosition; #endif /* Make sure there is no tracing while tracing! * In rare occasion, this may prevent to record * some tracing in a multithread environment. * We can live with that compromise. */ if( !TA_IsTraceEnabled() ) return; #if !defined( TA_SINGLE_THREAD ) retCode = TA_SemaWait( &global->callSema ); if( retCode != TA_SUCCESS ) return; #endif #ifdef TA_DEBUG /* If this position is already in the dictionary, just * increment the 'repetition' counter, else create * a new entry in the dictionary. */ TA_TraceDisable(); tracePosition = TA_DictGetValue_S( global->functionCalled, TA_StringToChar(key) ); TA_TraceEnable(); if( tracePosition ) tracePosition->repetition++; else { tracePosition = newTracePosition( funcname, filename, lineNb ); if( !tracePosition ) { #if !defined( TA_SINGLE_THREAD ) TA_SemaPost( &global->callSema ); #endif return; } TA_TraceDisable(); TA_DictAddPair_S( global->functionCalled, key, (void *)tracePosition ); TA_TraceEnable(); } /* Trace position are never deleted, until the library is shutdown. * Make a copy of it in the circular buffer. */ global->codeTrace[global->posForNextTrace] = *tracePosition; #else (void)key; /* Used only when doing debug. */ global->codeTrace[global->posForNextTrace].filename = filename; global->codeTrace[global->posForNextTrace].funcname = funcname; global->codeTrace[global->posForNextTrace].lineNb = lineNb; global->codeTrace[global->posForNextTrace].repetition = 1; #endif /* Move to the next entry in the circular buffer. */ global->posForNextTrace++; if( global->posForNextTrace >= TA_CODE_TRACE_SIZE ) global->posForNextTrace = 0; #if !defined( TA_SINGLE_THREAD ) TA_SemaPost( &global->callSema ); #endif }
TA_RetCode TA_SIMULATOR_OpenSource( const TA_AddDataSourceParamPriv *param, TA_DataSourceHandle **handle ) { TA_PROLOG TA_DataSourceHandle *tmpHandle; TA_PrivateHandle *privData; TA_StringCache *stringCache; *handle = NULL; TA_TRACE_BEGIN( TA_SIMULATOR_OpenSource ); stringCache = TA_GetGlobalStringCache(); if( !stringCache ) { TA_TRACE_RETURN( TA_INTERNAL_ERROR(91) ); } /* Verify that the requested functionality is supported or not. */ if( param->flags & TA_REPLACE_ZERO_PRICE_BAR ) { TA_TRACE_RETURN( TA_NOT_SUPPORTED ); } /* Allocate and initialize the handle. This function will also allocate the * private handle (opaque data). */ tmpHandle = (TA_DataSourceHandle *)TA_Malloc( sizeof( TA_DataSourceHandle ) ); if( tmpHandle == NULL ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } memset( tmpHandle, 0, sizeof( TA_DataSourceHandle ) ); privData = (TA_PrivateHandle *)TA_Malloc( sizeof( TA_PrivateHandle ) ); if( !privData ) { TA_Free( tmpHandle ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } memset( privData, 0, sizeof( TA_PrivateHandle ) ); tmpHandle->opaqueData = privData; /* Copy some parameters in the private handle. */ privData->categoryIter = 0; privData->catRefIter = 0; privData->catMrgIter = 0; if( param->info == NULL ) { privData->mrgInstance = 0; tmpHandle->nbCategory = 1; } else { privData->mrgInstance = atoi( TA_StringToChar( param->info ) ); if( (privData->mrgInstance < 1) || (privData->mrgInstance > 4) ) { TA_Free( tmpHandle ); TA_Free( privData ); TA_TRACE_RETURN( TA_BAD_PARAM ); } tmpHandle->nbCategory = 2; /* Allocate the data. */ } /* Pre-allocate all the string used in this data source. */ privData->ta_sim_ref_cat = TA_StringAlloc(stringCache, "TA_SIM_REF"); privData->ta_sim_mrg_cat = TA_StringAlloc(stringCache, "TA_SIM_MRG"); privData->daily_ref_0 = TA_StringAlloc(stringCache, "DAILY_REF_0"); privData->intra_ref_0 = TA_StringAlloc(stringCache, "INTRA_REF_0"); privData->mrg_0 = TA_StringAlloc(stringCache, "MRG_0"); if( !privData->ta_sim_ref_cat || !privData->ta_sim_mrg_cat || !privData->daily_ref_0 || !privData->intra_ref_0 || !privData->mrg_0 ) { freePrivateHandle( privData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Everything is fine, return the handle to the caller. */ *handle = tmpHandle; TA_TRACE_RETURN( TA_SUCCESS ); }
TA_RetCode TA_TradeLogAdd( TA_TradeLog *tradeLog, const TA_Transaction *newTransaction ) { TA_TradeLogPriv *tradeLogPriv; TA_Instrument *id; TA_Dict *theDict; TA_DataLog *dataLog; TA_TradeDictEntry *dictEntry; TA_StringCache *stringCache; TA_String *catString; TA_String *symString; const char *catCharPtr; const char *symCharPtr; TA_RetCode retCode; int quantity, entryTradeQuantity; TA_List *entryListToUse; TA_DataLog *entryTradeLog; TA_Real highestLow, highestHigh, lowestLow, lowestHigh; TA_Real entryPrice, tempReal; int i; retCode = TA_INTERNAL_ERROR(120); /* This function will transform the TA_Transaction into * an "entry" or multiple "trades" (because an exit can * be translated into multiple trade if there was multiple * entry point). */ if( !tradeLog || !newTransaction ) return TA_BAD_PARAM; /* Check that the TA_Transaction makes sense. */ if( (newTransaction->price <= 0.0) || (newTransaction->quantity <= 0) || (TA_TimestampValidate(&newTransaction->timestamp) != TA_SUCCESS) || (newTransaction->type >= TA_NB_TRADE_TYPE)) return TA_BAD_PARAM; /* Get access to the hidden data of the TA_TradeLog. */ tradeLogPriv = (TA_TradeLogPriv *)tradeLog->hiddenData; /* Make sure this is a valid object. */ if( !tradeLogPriv || (tradeLogPriv->magicNb != TA_TRADELOGPRIV_MAGIC_NB) ) return TA_BAD_OBJECT; /* Find the TA_TradeDictEntry corresponding to * the TA_Instrument. * * Use the dictionary corresponding to the type of * key of the TA_Instrument. * * If TA_Instrument is NULL, use the pre-allocated * default TA_TradeDictEntry. */ id = newTransaction->id; if( !id ) { dictEntry = &tradeLogPriv->defaultDictEntry; catCharPtr = NULL; symCharPtr = NULL; theDict = NULL; } else { catCharPtr = id->catString; symCharPtr = id->symString; if( catCharPtr ) { if( symCharPtr ) { theDict = tradeLogPriv->tradeDictCATSYM; dictEntry = TA_DictGetValue_S2( theDict, catCharPtr, symCharPtr ); } else { theDict = tradeLogPriv->tradeDictCAT; dictEntry = TA_DictGetValue_S( theDict, catCharPtr ); } } else if( symCharPtr ) { theDict = tradeLogPriv->tradeDictCAT; dictEntry = TA_DictGetValue_S( theDict, symCharPtr ); } else { theDict = tradeLogPriv->tradeDictUserKey; dictEntry = TA_DictGetValue_I( theDict, id->userKey ); } } if( !dictEntry ) { if( !theDict ) return TA_INTERNAL_ERROR(146); /* The TA_TradeDictEntry was not found, create it! */ dictEntry = TA_Malloc( sizeof( TA_TradeDictEntry ) ); if( !dictEntry ) return TA_ALLOC_ERR; memset( &dictEntry->id, 0, sizeof(TA_Instrument) ); TA_ListInit( &dictEntry->shortEntryPrivList ); TA_ListInit( &dictEntry->longEntryPrivList ); /* Add the dictEntry to the corresponding dictionary. */ stringCache = TA_GetGlobalStringCache(); if( catCharPtr ) { catString = TA_StringAlloc( stringCache, catCharPtr ); if( !catString ) { TA_Free( dictEntry ); return TA_ALLOC_ERR; } if( symCharPtr ) { symString = TA_StringAlloc( stringCache, symCharPtr ); if( !symString ) { TA_Free( dictEntry ); TA_StringFree( stringCache, catString ); return TA_ALLOC_ERR; } retCode = TA_DictAddPair_S2( theDict, catString, symString, dictEntry ); dictEntry->id.symString = TA_StringToChar(symString); } else retCode = TA_DictAddPair_S( theDict, catString, dictEntry ); dictEntry->id.catString = TA_StringToChar(catString); } else if( symCharPtr ) { symString = TA_StringAlloc( stringCache, symCharPtr ); if( !symString ) { TA_Free( dictEntry ); return TA_ALLOC_ERR; } retCode = TA_DictAddPair_S( theDict, symString, dictEntry ); dictEntry->id.symString = TA_StringToChar(symString); } else { retCode = TA_DictAddPair_I( theDict, id->userKey, dictEntry ); dictEntry->id.userKey = id->userKey; } /* Check the retCode of the TA_DictAddXXXXX function. */ if( retCode != TA_SUCCESS ) { TA_Free( dictEntry ); return TA_ALLOC_ERR; } } /* Identify the approriate list of entry. */ switch( newTransaction->type ) { case TA_LONG_ENTRY: case TA_LONG_EXIT: entryListToUse = &dictEntry->longEntryPrivList; break; case TA_SHORT_ENTRY: case TA_SHORT_EXIT: entryListToUse = &dictEntry->shortEntryPrivList; break; default: return TA_BAD_PARAM; } /* The sign of the quantity indicates if the * data log is a completed trade (+) or an * entry (-). */ switch( newTransaction->type ) { case TA_LONG_ENTRY: case TA_SHORT_ENTRY: /* Allocate a data log and add it to the list. */ dataLog = TA_AllocatorForDataLog_Alloc( &tradeLogPriv->allocator ); if( !dataLog ) return TA_ALLOC_ERR; dataLog->u.entry.quantity = -(newTransaction->quantity); dataLog->u.entry.entryPrice = newTransaction->price; TA_TimestampCopy( &dataLog->u.entry.entryTimestamp, &newTransaction->timestamp ); TA_ListNodeAddTail( entryListToUse, &dataLog->u.entry.node, dataLog ); break; case TA_LONG_EXIT: case TA_SHORT_EXIT: /* Invalidate cached calculation of this trade log. */ tradeLogPriv->flags &= ~TA_PMVALUECACHE_CALCULATED; /* Transform this transaction into one or * multiple trade(s). */ entryTradeLog = TA_ListRemoveHead( entryListToUse ); if( !entryTradeLog ) return TA_ENTRY_TRANSACTION_MISSING; quantity = newTransaction->quantity; while( quantity ) { entryTradeQuantity = -entryTradeLog->u.trade.quantity; if( entryTradeQuantity == quantity ) { /* This entry have exactly the right amount of * position for what needs to be closed. * Just transform the entry into a trade. */ entryTradeLog->u.trade.quantity = quantity; entryTradeLog->u.trade.id = id; TA_TimestampCopy( &entryTradeLog->u.trade.exitTimestamp, &newTransaction->timestamp ); /* Calculate the profit and make the entryPrice * negative if this is a short trade. * Both are multiplied by the quantity being * traded. */ entryPrice = entryTradeLog->u.entry.entryPrice; if( newTransaction->type == TA_LONG_EXIT ) { entryTradeLog->u.trade.profit = (newTransaction->price-entryPrice)*quantity; CALC_EXCURSION_LONG; } else { entryTradeLog->u.trade.profit = (entryPrice-newTransaction->price)*quantity; entryPrice = -entryPrice; CALC_EXCURSION_SHORT; } entryTradeLog->u.entry.entryPrice = entryPrice * quantity; return TA_SUCCESS; /* Done! */ } else if( entryTradeQuantity < quantity ) { /* This entry have less than the amount of * position that needs to be closed. * Just transform the entry into a trade * and move to the next entry. */ entryTradeLog->u.trade.quantity = entryTradeQuantity; quantity -= entryTradeQuantity; entryTradeLog->u.trade.id = id; TA_TimestampCopy( &entryTradeLog->u.trade.exitTimestamp, &newTransaction->timestamp ); /* Calculate the profit and make the entryPrice * negative if this is a short trade. * Both are multiplied by the quantity being * traded. */ entryPrice = entryTradeLog->u.trade.entryPrice; if( newTransaction->type == TA_LONG_EXIT ) { entryTradeLog->u.trade.profit = (newTransaction->price-entryPrice)*entryTradeQuantity; CALC_EXCURSION_LONG; } else { entryTradeLog->u.trade.profit = (entryPrice-newTransaction->price)*entryTradeQuantity; entryPrice = -entryPrice; CALC_EXCURSION_SHORT; } entryTradeLog->u.trade.entryPrice = entryPrice * entryTradeQuantity; /* Move to the next entry. If none available, that means there * was more "exit" than "entry" and this is considered an * error. */ entryTradeLog = TA_ListRemoveHead( entryListToUse ); if( !entryTradeLog ) return TA_ENTRY_TRANSACTION_MISSING; } else { /* This entry have more position than what the * exit requires, so the entry must be preserved. * Consequently, a new tradeLog must be allocated. */ dataLog = TA_AllocatorForDataLog_Alloc( &tradeLogPriv->allocator ); if( !dataLog ) return TA_ALLOC_ERR; TA_TimestampCopy( &dataLog->u.trade.entryTimestamp, &entryTradeLog->u.trade.entryTimestamp ); TA_TimestampCopy( &dataLog->u.trade.exitTimestamp, &newTransaction->timestamp ); dataLog->u.trade.quantity = quantity; entryPrice = entryTradeLog->u.trade.entryPrice; if( newTransaction->type == TA_LONG_EXIT ) { dataLog->u.trade.profit = (newTransaction->price-entryPrice)*quantity; CALC_EXCURSION_LONG; } else { dataLog->u.trade.profit = (entryPrice-newTransaction->price)*quantity; entryPrice = -entryPrice; CALC_EXCURSION_SHORT; } dataLog->u.trade.entryPrice = entryPrice*quantity; dataLog->u.trade.id = id; /* Adjust the entry and put it back for being process * again later. */ entryTradeLog->u.trade.quantity += quantity; TA_ListNodeAddHead( entryListToUse, &entryTradeLog->u.entry.node, entryTradeLog ); return TA_SUCCESS; /* Done! */ } } break; default: return TA_INTERNAL_ERROR(121); } return TA_SUCCESS; }
TA_RetCode TA_FileIndexAddSymbolData( TA_Libc *libHandle, TA_FileIndexCategoryData *categoryData, TA_String *stringSymbol, TA_ValueTreeNode *treeNodeValue, TA_FileIndexSymbolData **added ) { TA_PROLOG; TA_RetCode retCode; TA_FileIndexSymbolData *symbolData; unsigned int tmpInt; unsigned int symbolFound; /* Boolean */ TA_StringCache *stringCache; TA_TRACE_BEGIN( libHandle, TA_FileIndexAddSymbolData ); stringCache = TA_GetGlobalStringCache( libHandle ); TA_ASSERT( libHandle, categoryData != NULL ); TA_ASSERT( libHandle, categoryData->listSymbol != NULL ); TA_ASSERT( libHandle, stringSymbol != NULL ); TA_ASSERT( libHandle, treeNodeValue != NULL ); if( added ) *added = NULL; /* Trap the case where this symbol is already there for that * category. In that case, the information is ignored. * Under the same category, for the same datasource, only one file * is supported for a category-symbol pair. */ symbolData = (TA_FileIndexSymbolData *)TA_ListAccessTail( categoryData->listSymbol ); symbolFound = 0; while( symbolData && !symbolFound ) { TA_ASSERT( libHandle, symbolData->string != NULL ); tmpInt = strcmp( TA_StringToChar( stringSymbol ), TA_StringToChar( symbolData->string ) ); if( tmpInt == 0 ) symbolFound = 1; else symbolData = (TA_FileIndexSymbolData *)TA_ListAccessPrev( categoryData->listSymbol ); } if( !symbolFound ) { /* This is a new symbol, so allocate the TA_FileIndexSymbolData */ symbolData = (TA_FileIndexSymbolData *)TA_Malloc( libHandle, sizeof(TA_FileIndexSymbolData) ); if( !symbolData ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Initialize the TA_FileIndexSymbolData */ symbolData->parent = categoryData; symbolData->node = treeNodeValue; symbolData->string = TA_StringDup( stringCache, stringSymbol ); if( !symbolData->string ) { TA_Free( libHandle, symbolData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Add it to the category list. */ retCode = TA_ListAddTail( categoryData->listSymbol, symbolData ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, symbolData->string ); TA_Free( libHandle, symbolData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } } /* Return the address of the object representing that symbol. */ if( added ) *added = symbolData; TA_TRACE_RETURN( TA_SUCCESS ); }
TA_RetCode TA_FileIndexAddCategoryData( TA_FileIndexPriv *data, TA_String *stringCategory, TA_FileIndexCategoryData **added ) { TA_PROLOG; TA_RetCode retCode; TA_FileIndexCategoryData *categoryData; unsigned int tmpInt; unsigned int categoryFound; /* Boolean */ TA_Libc *libHandle; TA_StringCache *stringCache; libHandle = data->libHandle; TA_TRACE_BEGIN( libHandle, TA_FileIndexAddCategoryData ); stringCache = TA_GetGlobalStringCache( libHandle ); TA_ASSERT( libHandle, data != NULL ); TA_ASSERT( libHandle, stringCategory != NULL ); /* Trap the case where the category is already added. */ categoryData = (TA_FileIndexCategoryData *)TA_ListAccessTail( data->listCategory ); categoryFound = 0; while( categoryData && !categoryFound ) { TA_ASSERT( libHandle, categoryData->string != NULL ); tmpInt = strcmp( TA_StringToChar( stringCategory ), TA_StringToChar( categoryData->string ) ); if( tmpInt == 0 ) categoryFound = 1; else categoryData = (TA_FileIndexCategoryData *)TA_ListAccessPrev( data->listCategory ); } if( !categoryFound ) { /* This is a new category, so allocate the TA_FileIndexCategoryData */ categoryData = (TA_FileIndexCategoryData *)TA_Malloc( libHandle, sizeof(TA_FileIndexCategoryData) ); if( !categoryData ) TA_TRACE_RETURN( TA_ALLOC_ERR ); /* Initialize the TA_FileIndexCategoryData */ categoryData->parent = data; if( stringCategory ) { categoryData->string = TA_StringDup( stringCache, stringCategory); /* String for this category. Can be NULL. */ if( !categoryData->string ) { TA_Free( libHandle, categoryData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } } else categoryData->string = NULL; categoryData->listSymbol = TA_ListAlloc( libHandle ); if( !categoryData->listSymbol ) { if( categoryData->string ) TA_StringFree( stringCache, categoryData->string ); TA_Free( libHandle, categoryData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Add it to the TA_FileIndexPriv */ retCode = TA_ListAddTail( data->listCategory, categoryData ); if( retCode != TA_SUCCESS ) { TA_ListFree( categoryData->listSymbol ); if( categoryData->string ) TA_StringFree( stringCache, categoryData->string ); TA_Free( libHandle, categoryData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } #if 0 printf( "**ADDING CATEGORY[%s]\n", TA_StringToChar( categoryData->string ) ); #endif } /* Return the address of the object representing that category. */ if( added ) *added = categoryData; TA_TRACE_RETURN( TA_SUCCESS ); }
TA_RetCode TA_ReadOpInfoAlloc( const char *sourceInfo, TA_ReadOpInfo **allocatedInfo, unsigned int readOpFlags ) { TA_PROLOG TA_RetCode retCode; TA_ReadOp readOp; TA_ReadOp *arrayReadOp; TA_ReadOpInfo *newReadOpInfo; TA_Field fieldMask, fieldProvided; unsigned int timeframeIdx; unsigned int intraDayIncPeriod; TA_TokenId intraDayIncToken; TA_TokenId tempToken; unsigned int tempInt; unsigned int period; unsigned int errorOccurred; const char *pos; unsigned int inField; unsigned int nbField; unsigned int nbCharInField; unsigned int skipNonDigitLine; const char *ptrFirstCarInField; unsigned char localBuf[10]; unsigned int bufIdx, opIdx, i; register unsigned int flagSet; register TA_ReadOp *ptrReadOp; TA_TRACE_BEGIN( TA_BuildArrayReadOp ); newReadOpInfo = (TA_ReadOpInfo *)TA_Malloc( sizeof( TA_ReadOpInfo ) ); /* These variables are resolved within this function. */ memset( newReadOpInfo, 0, sizeof( TA_ReadOpInfo ) ); /* At this point, TA_ReadOpInfoFree can be safely called. */ /* Keep track of some user provided parameter. */ newReadOpInfo->sourceInfo = sourceInfo; newReadOpInfo->readOpFlags = readOpFlags; /* Initialize some defaults. */ newReadOpInfo->openInterestMult = 100; newReadOpInfo->volumeMult = 100; nbField = 0; intraDayIncPeriod = 0; intraDayIncToken = TA_TOK_END; pos = newReadOpInfo->sourceInfo; if( !pos || (*pos == '\0') ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_MISSING_FIELD ); } /* Find how many fields are defined and check some syntax * at the same time. */ if( *pos != '[' ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } inField = 0; nbCharInField = 0; skipNonDigitLine = 0; ptrFirstCarInField = NULL; while( *pos != '\0' ) { switch( *pos ) { case '[': if( inField ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } inField = 1; break; case ']': if( (!inField) || (nbCharInField == 0) ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } nbField++; /* Exclude fields not generating a TA_ReadOp. * For the time being that means only the -H and -NDL field. */ if( nbCharInField >= 2 ) { TA_ASSERT( ptrFirstCarInField != NULL ); if( ptrFirstCarInField[0] == '-' ) { if( toupper(ptrFirstCarInField[1]) == 'H' ) nbField--; else if( (toupper(ptrFirstCarInField[1]) == 'N') && (toupper(ptrFirstCarInField[2]) == 'D') && (toupper(ptrFirstCarInField[3]) == 'L') ) { skipNonDigitLine = 1; nbField--; } } } inField = 0; nbCharInField = 0; ptrFirstCarInField = NULL; break; default: if( !inField ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } if( nbCharInField == 0 ) ptrFirstCarInField = pos; nbCharInField++; break; } pos++; } if( inField || *(pos-1) != ']' ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } /* Build the TA_ReadOp array */ arrayReadOp = (TA_ReadOp *)TA_Malloc( sizeof( TA_ReadOp ) * nbField ); if( !arrayReadOp ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } newReadOpInfo->arrayReadOp = arrayReadOp; pos = TA_StringToChar(newReadOpInfo->sourceInfo); bufIdx = 0; opIdx = 0; while( *pos != '\0' && (opIdx < nbField) ) { switch( *pos ) { case '[': break; case ']': localBuf[bufIdx] ='\0'; bufIdx = 0; /* Identify the field and build the TA_ReadOp. */ tempInt = 0; retCode = buildReadOp( newReadOpInfo, (const char *)&localBuf[0], &arrayReadOp[opIdx], &tempToken, &tempInt ); if( retCode != TA_SUCCESS ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( retCode ); } if( arrayReadOp[opIdx] != 0 ) { /* Set the replace zero flag as needed */ if( TA_IS_REPLACE_ZERO(readOpFlags) && TA_IS_REAL_CMD(arrayReadOp[opIdx]) ) { TA_SET_REPLACE_ZERO(arrayReadOp[opIdx]); } /* Set the skipNonDigitLine flag as needed. */ if( skipNonDigitLine == 1 ) { TA_SET_SKIP_NDL_FLAG(arrayReadOp[opIdx]); } /* Ooof... this readOp is now all build! */ opIdx++; } /* If this is a time token, make sure this * is not in contradiction with an already * specified increment. */ if( intraDayIncPeriod ) { errorOccurred = 0; switch( tempToken ) { case TA_TOK_SEC: case TA_TOK_SS: if( (intraDayIncToken == TA_TOK_MIN) || (intraDayIncToken == TA_TOK_MN) ) errorOccurred = 1; /* no break */ case TA_TOK_MIN: case TA_TOK_MN: if( (intraDayIncToken == TA_TOK_HOUR) || (intraDayIncToken == TA_TOK_HH) ) errorOccurred = 1; break; case TA_TOK_HOUR: case TA_TOK_HH: errorOccurred = 1; break; default: /* Do nothing */ break; } if( errorOccurred ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } } /* Check if a period increment is specified. */ if( (tempInt != 0) && (tempInt != 1) ) { if( intraDayIncPeriod != 0 ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } intraDayIncPeriod = tempInt; intraDayIncToken = tempToken; } break; default: if( bufIdx >= sizeof(localBuf)-1 ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } localBuf[bufIdx++] = *pos; break; } pos++; } if( opIdx != nbField ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(89) ); } arrayReadOp[opIdx-1] |= TA_CMD_LAST_FLAG; /* Build the mask representing the fields provided. */ fieldProvided = 0; timeframeIdx = TA_INTEGER_ARRAY_SIZE; for( opIdx=0; opIdx < nbField; opIdx++ ) { readOp = arrayReadOp[opIdx]; TA_ASSERT( readOp != 0 ); /* Parano test */ if( !TA_IS_PERMANENT_SKIP_SET(readOp) ) { /* Make sure this field was not specified twice. */ for( i=opIdx+1; i < nbField; i++ ) { if( (TA_IS_REAL_CMD(readOp) && TA_IS_REAL_CMD(arrayReadOp[i])) || (TA_IS_INTEGER_CMD(readOp) && TA_IS_INTEGER_CMD(arrayReadOp[i])) ) { if( (TA_GET_IDX(readOp) == TA_GET_IDX(arrayReadOp[i])) && !TA_IS_PERMANENT_SKIP_SET(arrayReadOp[i]) ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_REDUNDANT_FIELD ); } } } /* Parano test: Double-check redundant field in a different way. */ fieldMask = TA_ReadOpToField( readOp ); TA_ASSERT( fieldMask != 0 ); if( !(fieldMask & TA_TIMESTAMP) && (fieldProvided & fieldMask) ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_REDUNDANT_FIELD ); } /* Set the field. */ fieldProvided |= fieldMask; /* Keep track of the smallest granularity of the timestamp. */ if( fieldMask & TA_TIMESTAMP ) { if( (timeframeIdx == TA_INTEGER_ARRAY_SIZE) || (TA_GET_IDX(readOp) < timeframeIdx) ) timeframeIdx = TA_GET_IDX(readOp); } } } /* No date/time reference provided!? This is considered an error * in the current implementation. */ if( timeframeIdx == TA_INTEGER_ARRAY_SIZE ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_MISSING_DATE_OR_TIME_FIELD ); } /* Determine at which point the timestamp is completed. */ flagSet = 0; for( opIdx=nbField; opIdx > 0; opIdx-- ) { ptrReadOp = &arrayReadOp[opIdx-1]; readOp = *ptrReadOp; if( !flagSet && TA_IS_INTEGER_CMD(readOp) && (TA_GET_IDX(readOp)<=TA_YEAR_IDX) && !TA_IS_PERMANENT_SKIP_SET(readOp) ) { TA_SET_TIMESTAMP_COMPLETE(*ptrReadOp); flagSet = 1; } else { TA_CLR_TIMESTAMP_COMPLETE(*ptrReadOp); } } /* Validate and identify the period. */ period = 0; if( intraDayIncPeriod ) { errorOccurred = 0; switch( timeframeIdx ) { case TA_YEAR_IDX: case TA_MONTH_IDX: case TA_DAY_IDX: errorOccurred = 1; break; case TA_HOUR_IDX: if( (intraDayIncPeriod < TA_1HOUR) || (intraDayIncPeriod >= TA_DAILY) ) errorOccurred = 1; break; case TA_MIN_IDX: if( (intraDayIncPeriod < TA_1MIN) || (intraDayIncPeriod >= TA_1HOUR) ) errorOccurred = 1; break; case TA_SEC_IDX: if( (intraDayIncPeriod < TA_1SEC) || (intraDayIncPeriod >= TA_1MIN) ) errorOccurred = 1; break; default: TA_ReadOpInfoFree( newReadOpInfo ); TA_FATAL( NULL, timeframeIdx, fieldProvided ); } if( errorOccurred ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } period = intraDayIncPeriod; } else { switch( timeframeIdx ) { case TA_YEAR_IDX: period = TA_YEARLY; break; case TA_MONTH_IDX: period = TA_MONTHLY; break; case TA_DAY_IDX: period = TA_DAILY; break; case TA_HOUR_IDX: period = TA_1HOUR; break; case TA_MIN_IDX: period = TA_1MIN; break; case TA_SEC_IDX: period = TA_1SEC; break; default: TA_FATAL( NULL, timeframeIdx, fieldProvided ); } } /* A last check... */ if( period == 0 ) { TA_ReadOpInfoFree( newReadOpInfo ); TA_TRACE_RETURN( TA_INVALID_FIELD ); } /* Everything is fine, let's return the information. */ newReadOpInfo->arrayReadOp = arrayReadOp; newReadOpInfo->fieldProvided = fieldProvided; newReadOpInfo->nbReadOp = nbField; newReadOpInfo->period = period; *allocatedInfo = newReadOpInfo; TA_TRACE_RETURN( TA_SUCCESS ); }
/**** Local functions definitions. ****/ static TA_RetCode translateToYahooName( TA_Libc *libHandle, const TA_String *categoryName, const TA_String *symbolName, char *buffer, unsigned int maxBufferSize ) { TA_CountryId countryId; unsigned int i, symbolLength, xchangeLength, extLength, typeLength; const char *symbol; const char *xchange; const char *type; const char *category; const char *ext; (void)libHandle; category = TA_StringToChar(categoryName); /* Identify the symbol and its length. */ symbol = TA_StringToChar(symbolName); symbolLength = strlen( symbol ); if( symbolLength >= maxBufferSize ) return TA_INVALID_SECURITY_SYMBOL; strcpy( buffer, symbol ); /* Trap special case where the symbol/category is for * one of the hard coded indice. */ for( i=0; i < hardCodedIndiceSize; i++ ) { if( (strcmp( hardCodedIndice[i].symbolLib, TA_StringToChar( symbolName ) ) == 0) && (strcmp( hardCodedIndice[i].categoryLib, TA_StringToChar( categoryName ) ) == 0) ) { strncpy( buffer, hardCodedIndice[i].symbolYahoo, maxBufferSize ); return TA_SUCCESS; } } /* Identify if an extenstion must be appended * to the symbol. * At the same time, verify the country. */ countryId = TA_CountryAbbrevToId( TA_StringToChar(categoryName) ); switch( countryId ) { case TA_Country_ID_CA: /* Canada */ case TA_Country_ID_US: /* United States */ case TA_Country_ID_UK: /* United Kingdom */ case TA_Country_ID_DE: /* Germany */ case TA_Country_ID_DK: /* Denmark */ case TA_Country_ID_ES: /* Spain */ case TA_Country_ID_FR: /* France */ case TA_Country_ID_IT: /* Italy */ case TA_Country_ID_SE: /* Sweden */ case TA_Country_ID_NO: /* Norway */ /* Get the length of the second field of the category. * This field must exist, else an error is returned. */ xchangeLength = getstrfldlen( category, 1, 0, "." ) -1; if( xchangeLength < 2 ) return TA_INVALID_SECURITY_EXCHANGE; xchange = strchr( category, '.' ); if( !xchange || (*xchange == '\0') ) return TA_INVALID_SECURITY_EXCHANGE; xchange++; if( *xchange == '\0' ) return TA_INVALID_SECURITY_EXCHANGE; /* Get the length of the third field of the category. * This field must exist, else an error is returned. */ type = strchr( xchange, '.' ); if( !type || (type == xchange) || (*type == '\0') ) return TA_INVALID_SECURITY_TYPE; type++; if( *type == '\0' ) return TA_INVALID_SECURITY_TYPE; typeLength = strlen( type ); /* Look for the possible extension corresponding to * this exchange. If found, append it to the symbol. */ for( i=0; i < NB_YAHOO_EXCHANGE_EXTENSION; i++ ) { if( (TA_YahooExtensionTable[i].countryId == countryId) && (lexncmp( TA_YahooExtensionTable[i].exchange, xchange, xchangeLength ) == 0) && (lexncmp( TA_YahooExtensionTable[i].type, type, typeLength ) == 0) ) { ext = TA_YahooExtensionTable[i].extension; extLength = strlen( ext ); if( extLength+symbolLength+1 > maxBufferSize-1 ) return TA_INVALID_SECURITY_EXCHANGE; buffer[symbolLength] = '.'; symbolLength++; strcpy( &buffer[symbolLength], ext ); break; /* Exit the loop */ } } break; default: return TA_INVALID_SECURITY_COUNTRY; } /* At this point, buffer contains the name as used by Yahoo! web site. */ return TA_SUCCESS; }