/**** Global functions definitions. ****/ TA_Dict *TA_DictAlloc( TA_Libc *libHandle, unsigned int flags, void (*freeValueFunc)( TA_Libc *libHandle, void *) ) { TA_PrivDictInfo *theDict; if( !libHandle ) return NULL; /* Alloc the structure used as an handle for this dictionary. */ theDict = (TA_PrivDictInfo *) TA_Malloc( libHandle, sizeof( TA_PrivDictInfo ) ); if( !theDict ) return NULL; theDict->flags = flags; /* Create the Kazlib dictionary. */ if( flags & TA_DICT_KEY_ONE_STRING ) dict_init( libHandle, &theDict->d, DICTCOUNT_T_MAX, compareFunction_S ); else if( flags & TA_DICT_KEY_TWO_STRING ) dict_init( libHandle, &theDict->d, DICTCOUNT_T_MAX, compareFunction_S ); else if( flags & TA_DICT_KEY_INTEGER ) dict_init( libHandle, &theDict->d, DICTCOUNT_T_MAX, compareFunction_I ); /* Keep a copy of the freeValueFunc pointer for later use. */ theDict->freeValueFunc = freeValueFunc; /* Remember to which memory context we belong. */ theDict->libHandle = libHandle; return (TA_Dict *)theDict; }
/**** Global functions definitions. ****/ TA_Dict *TA_DictAlloc( unsigned int flags, void (*freeValueFunc)( void *) ) { TA_PrivDictInfo *theDict; /* Alloc the structure used as an handle for this dictionary. */ theDict = (TA_PrivDictInfo *) TA_Malloc( sizeof( TA_PrivDictInfo ) ); if( !theDict ) return NULL; theDict->flags = flags; /* Create the Kazlib dictionary. */ if( flags & TA_DICT_KEY_ONE_STRING ) dict_init( &theDict->d, DICTCOUNT_T_MAX, compareFunction_S ); else if( flags & TA_DICT_KEY_TWO_STRING ) dict_init( &theDict->d, DICTCOUNT_T_MAX, compareFunction_S ); else if( flags & TA_DICT_KEY_INTEGER ) dict_init( &theDict->d, DICTCOUNT_T_MAX, compareFunction_I ); /* Keep a copy of the freeValueFunc pointer for later use. */ theDict->freeValueFunc = freeValueFunc; return (TA_Dict *)theDict; }
/* A little utility to fetch a web page and send the raw data * to the provided FILE pointer. This ptr could be "stdout" to * display on the console. * * Example: * TA_WebFetch( "www.yahoo.com", stdout ); * or * TA_WebFetch( "http://www.yahoo.com/mt", myFile ); */ TA_RetCode TA_WebFetch( TA_Libc *libHandle, const char *url, FILE *out ) { TA_RetCode retCode; TA_WebPage *webPage; const char *stringStart; const char *webSitePage; char *allocString; unsigned int webSiteLength; if( !url ) return TA_BAD_PARAM; allocString = NULL; /* Skip the http:// if specified. */ stringStart = url; if( strncmp( url, "http://", 7 ) == 0 ) stringStart += 7; /* Find if there is a specifc web page specified. */ webSitePage = strchr( stringStart, '/' ); if( webSitePage ) { webSitePage++; if( *webSitePage == '\0' ) { webSitePage = NULL; } else { webSiteLength = webSitePage - stringStart - 1; allocString = (char *)TA_Malloc( libHandle, webSiteLength+1 ); if( !allocString ) return TA_ALLOC_ERR; strncpy( allocString, stringStart, webSiteLength ); allocString[webSiteLength] = '\0'; stringStart = allocString; } } retCode = TA_WebPageAlloc( libHandle, stringStart, webSitePage, NULL, NULL, &webPage, 2 ); if( allocString ) TA_Free( libHandle, allocString ); if( retCode == TA_SUCCESS ) { retCode = TA_StreamToFile( webPage->content, out ); TA_WebPageFree( webPage ); } return retCode; }
/* Initialize the cache mechanism. */ TA_RetCode TA_StringCacheAlloc( TA_StringCache **newStringCache ) { TA_StringCachePriv *stringCachePriv; #if !defined( TA_SINGLE_THREAD ) TA_RetCode retCode; #endif if( !newStringCache ) return TA_BAD_PARAM; *newStringCache = NULL; stringCachePriv = (TA_StringCachePriv *)TA_Malloc( sizeof( TA_StringCachePriv ) ); if( !stringCachePriv ) return TA_ALLOC_ERR; memset( stringCachePriv, 0, sizeof( TA_StringCachePriv ) ); #if !defined( TA_SINGLE_THREAD ) retCode = TA_SemaInit( &stringCachePriv->sema, 1 ); if( retCode != TA_SUCCESS ) { TA_Free( stringCachePriv ); return retCode; } #endif /* Success, return the cache to the caller. */ *newStringCache = (TA_StringCache *)stringCachePriv; return TA_SUCCESS; }
/**** Global functions definitions. ****/ TA_RetCode TA_GroupTableAlloc( TA_StringTable **table ) { TA_StringTable *stringTable; TA_StringTablePriv *stringTablePriv; if( table == NULL ) { return TA_BAD_PARAM; } stringTable = (TA_StringTable *)TA_Malloc( sizeof(TA_StringTable) + sizeof(TA_StringTablePriv) ); if( !stringTable ) { *table = NULL; return TA_ALLOC_ERR; } memset( stringTable, 0, sizeof(TA_StringTable) + sizeof(TA_StringTablePriv) ); stringTablePriv = (TA_StringTablePriv *)(((char *)stringTable)+sizeof(TA_StringTable)); stringTablePriv->magicNumber = TA_STRING_TABLE_GROUP_MAGIC_NB; stringTable->size = TA_NB_GROUP_ID; stringTable->string = &TA_GroupString[0]; stringTable->hiddenData = stringTablePriv; /* From this point, TA_FuncTableFree can be safely called. */ /* Success. Return the table to the caller. */ *table = stringTable; return TA_SUCCESS; }
/**** Local functions definitions. ****/ static TA_RetCode rangeTestFunction( TA_Integer startIdx, TA_Integer endIdx, TA_Real *outputBuffer, TA_Integer *outBegIdx, TA_Integer *outNbElement, TA_Integer *lookback, void *opaqueData, unsigned int outputNb ) { TA_RetCode retCode; TA_RangeTestParam *testParam; TA_Real *out1; TA_Real *out2; TA_Real *dummyOutput; (void)outputNb; testParam = (TA_RangeTestParam *)opaqueData; dummyOutput = TA_Malloc( (endIdx-startIdx+1) * sizeof(TA_Real) ); if( outputNb == 0 ) { out1 = outputBuffer; out2 = dummyOutput; } else { out1 = dummyOutput; out2 = outputBuffer; } switch( testParam->test->theFunction ) { case TA_HT_PHASOR_TEST: retCode = TA_HT_PHASOR( startIdx, endIdx, testParam->price, outBegIdx, outNbElement, out1, out2 ); *lookback = TA_HT_PHASOR_Lookback(); break; case TA_HT_SINE_TEST: retCode = TA_HT_SINE( startIdx, endIdx, testParam->price, outBegIdx, outNbElement, out1, out2 ); *lookback = TA_HT_SINE_Lookback(); break; default: retCode = TA_INTERNAL_ERROR(132); } TA_Free(dummyOutput); return retCode; }
/* Allocate dyamically a copy of string. * Eliminate also all whitespace in the copy. */ TA_String *TA_StringAllocTrim( TA_StringCache *stringCache, const char *string ) { char *str; char *ptrCharTmp1; const char *ptrCharTmp2; unsigned int trimmedSize; unsigned int nonTrimmedSize; TA_StringCachePriv *stringCachePriv; if( stringCache == NULL ) return NULL; stringCachePriv = (TA_StringCachePriv *)stringCache; if( !string ) return NULL; /* Evaluate the size and see if any trimming is needed. */ trimmedSize = 0; nonTrimmedSize = 0; ptrCharTmp2 = string; while( *ptrCharTmp2++ != '\0' ) { if( !isspace(*ptrCharTmp2) ) trimmedSize++; nonTrimmedSize++; } if( trimmedSize == nonTrimmedSize ) return TA_StringAlloc( stringCache, string ); trimmedSize = (trimmedSize + 1)*sizeof( unsigned char ); str = (char *)TA_Malloc( trimmedSize ); if( str != NULL ) { /* Eliminate all whitespaces (not speed optimize...) */ ptrCharTmp1 = &str[0]; ptrCharTmp2 = string; while( *ptrCharTmp2 != '\0' ) { if( !isspace(*ptrCharTmp2) ) { *ptrCharTmp1 = *ptrCharTmp2; ptrCharTmp1++; } ptrCharTmp2++; } *ptrCharTmp1 = '\0'; return TA_StringAlloc( stringCache, str ); } /* Allocation error. */ return (TA_String *)NULL; }
TA_RetCode TA_PMAlloc( const TA_Timestamp *startDate, const TA_Timestamp *endDate, TA_Real initialCapital, TA_PM **allocatedPM ) { TA_PM *pm; TA_PMPriv *pmPriv; unsigned int delta; TA_RetCode retCode; /* Check all the parameters. */ if( !allocatedPM ) return TA_BAD_PARAM; *allocatedPM = NULL; if( !startDate || !endDate ) return TA_BAD_PARAM; if( TA_TimestampValidate( startDate ) ) return TA_BAD_START_DATE; if( TA_TimestampValidate( endDate ) || TA_TimestampGreater( startDate, endDate ) ) return TA_BAD_END_DATE; /* To keep things simple, it is assumed that * the requested date range contains at least * one weekday. */ retCode = TA_TimestampDeltaWeekday( startDate, endDate, &delta ); if( retCode != TA_SUCCESS ) return retCode; if( delta <= 0 ) return TA_NO_WEEKDAY_IN_DATE_RANGE; /* Allocate the public and private structure. */ pm = TA_Malloc( sizeof( TA_PM ) + sizeof( TA_PMPriv ) ); if( !pm ) return TA_ALLOC_ERR; memset( pm, 0, sizeof( TA_PM ) + sizeof( TA_PMPriv ) ); pmPriv = (TA_PMPriv *)(((char *)pm)+sizeof(TA_PM)); pmPriv->magicNb = TA_PMPRIV_MAGIC_NB; pmPriv->initialCapital = initialCapital; pm->hiddenData = pmPriv; TA_ListInit( &pmPriv->tradeLogList ); /* TA_PMFree can be safely called from this point. */ TA_TimestampCopy( &pmPriv->endDate, endDate ); TA_TimestampCopy( &pmPriv->startDate, startDate ); /* Success, return the allocated data to the caller. */ *allocatedPM = pm; return TA_SUCCESS; }
/**** Global functions definitions. ****/ TA_RetCode TA_GroupTableAlloc( TA_Libc *libHandle, TA_StringTable **table ) { TA_PROLOG; TA_StringTable *stringTable; TA_StringTableGroupHidden *stringTableHidden; TA_TRACE_BEGIN( libHandle, TA_GroupTableAlloc ); if( table == NULL ) { TA_TRACE_RETURN( TA_BAD_PARAM ); } *table = NULL; stringTable = (TA_StringTable *)TA_Malloc( libHandle, sizeof(TA_StringTable) ); if( !stringTable ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } stringTableHidden = (TA_StringTableGroupHidden *)TA_Malloc( libHandle, sizeof(TA_StringTableGroupHidden) ); if( !stringTableHidden ) { TA_Free( libHandle, stringTable ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } stringTableHidden->libHandle = libHandle; stringTableHidden->magicNb = TA_STRING_TABLE_GROUP_MAGIC_NB; stringTable->size = TA_NB_GROUP_ID; stringTable->string = &TA_GroupString[0]; stringTable->hiddenData = stringTableHidden; /* From this point, TA_FuncTableFree can be safely called. */ /* Success. Return the table to the caller. */ *table = stringTable; TA_TRACE_RETURN( TA_SUCCESS ); }
/**** Local functions definitions. ****/ static TA_RetCode TA_NetworkGlobalInit( void **globalToAlloc ) { #if !defined( TA_SINGLE_THREAD ) TA_RetCode retCode; #endif TA_NetworkGlobal *global; if( !globalToAlloc ) return TA_BAD_PARAM; *globalToAlloc = NULL; #if defined( USE_LIBCURL ) #if defined(WIN32) if( curl_global_init(CURL_GLOBAL_WIN32) != CURLE_OK ) return TA_LIBCURL_GLOBAL_INIT_FAILED; #else if( curl_global_init(CURL_GLOBAL_NOTHING) != CURLE_OK ) return TA_LIBCURL_GLOBAL_INIT_FAILED; #endif #endif global = (TA_NetworkGlobal *)TA_Malloc( sizeof( TA_NetworkGlobal ) ); if( !global ) return TA_ALLOC_ERR; memset( global, 0, sizeof( TA_NetworkGlobal ) ); #if defined( USE_LIBCURL ) global->curlHandle = curl_easy_init(); if( global->curlHandle == NULL ) { TA_Free( global ); return TA_LIBCURL_INIT_FAILED; } #endif #if !defined( TA_SINGLE_THREAD ) /* Initialize the mutex in a non-block state. */ retCode = TA_SemaInit( &global->mutexSema, 1 ); if( retCode != TA_SUCCESS ) { TA_Free( global ); return retCode; } #endif global->initialized = 1; /* Success, return the allocated memory to the caller. */ *globalToAlloc = global; return TA_SUCCESS; }
TA_RetCode TA_FileIndexAddTokenInfo( TA_FileIndexPriv *data, TA_TokenId id, TA_String *value, TA_TokenInfo *optBefore ) { TA_PROLOG; TA_RetCode retCode; TA_TokenInfo *info; TA_Libc *libHandle; TA_StringCache *stringCache; libHandle = data->libHandle; TA_TRACE_BEGIN( libHandle, TA_FileIndexAddTokenInfo ); stringCache = TA_GetGlobalStringCache( libHandle ); info = TA_Malloc( libHandle, sizeof( TA_TokenInfo ) ); if( !info ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } info->id = id; if( value == NULL ) info->value = NULL; else { info->value = TA_StringDup( stringCache, value ); if( !info->value ) { TA_Free( libHandle, info ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } } if( optBefore ) retCode = TA_ListAddBefore( data->listLocationToken, optBefore, info ); else retCode = TA_ListAddTail( data->listLocationToken, info ); if( retCode != TA_SUCCESS ) { if( info->value ) TA_StringFree( stringCache, info->value ); TA_Free( libHandle, info ); TA_TRACE_RETURN( retCode ); } TA_TRACE_RETURN( TA_SUCCESS ); }
size_t libcurlWriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) { register int bufferSize; TA_WebPage *webPage; TA_WebPageHiddenData *webPageHidden; TA_RetCode retCode; char *buffer; webPage = (TA_WebPage *)data; webPageHidden = (TA_WebPageHiddenData *)webPage->hiddenData; bufferSize = size * nmemb; /* Make a copy of the provided data. */ buffer = TA_Malloc( bufferSize ); if( !buffer ) return 0; /* Error. */ memcpy( buffer, ptr, bufferSize ); /* Add the data to the stream. */ if( !webPage->content ) { /* The first buffer will initiate the creation of * the stream. */ webPage->content = TA_StreamAllocFromBuffer( buffer, bufferSize, NULL, NULL ); if( !webPage->content ) { TA_Free( buffer ); return 0; /* Error. */ } } else { /* Add the buffer to the stream. */ retCode = TA_StreamAddBuffer( webPage->content, buffer, bufferSize, NULL, NULL ); if( retCode != TA_SUCCESS ) { TA_Free( buffer ); return 0; /* Error */ } } return bufferSize; }
/**** Local functions definitions. ****/ static TA_ValueTreeNode *allocTreeNode( TA_Libc *libHandle, TA_ValueTreeNode *parent, TA_String *string ) { TA_ValueTreeNode *node; TA_RetCode retCode; TA_StringCache *stringCache; stringCache = TA_GetGlobalStringCache( libHandle ); node = (TA_ValueTreeNode *)TA_Malloc( libHandle, sizeof( TA_ValueTreeNode ) ); if( !node ) return (TA_ValueTreeNode *)NULL; node->string = NULL; node->parent = NULL; node->child = TA_ListAlloc( libHandle ); if( !node->child ) { freeTreeNode( libHandle, node ); return NULL; } if( string ) { node->string = TA_StringDup( stringCache, string ); if( !node->string ) { freeTreeNode( libHandle, node ); return NULL; } } if( parent ) { retCode = TA_ListAddTail( parent->child, node ); if( retCode != TA_SUCCESS ) { freeTreeNode( libHandle, node ); return NULL; } node->parent = parent; } return node; }
static TA_TracePosition *newTracePosition( const char *funcname, const char *filename, unsigned int lineNb ) { TA_TracePosition *tracePosition; tracePosition = (TA_TracePosition *)TA_Malloc( sizeof(TA_TracePosition) ); if( !tracePosition ) return NULL; tracePosition->filename = filename; tracePosition->funcname = funcname; tracePosition->lineNb = lineNb; tracePosition->repetition = 1; return tracePosition; }
static TA_String *stringAllocInternal( const char *string, unsigned int newStringLength, TA_CharCase caseType ) { unsigned int size, i; char *str; /* Not in the cache, or could have reach duplicaton limit. * No choice to do an allocation. */ size = ( newStringLength + 2 ) * sizeof( unsigned char ); str = (char *)TA_Malloc( size ); if( str != NULL ) { if( caseType == UPPER_CASE ) { for( i=0; i < newStringLength; i++ ) str[i+1] = (unsigned char)toupper( string[i] ); } else if( caseType == PATH ) { for( i=0; i < newStringLength; i++ ) { if( TA_IsSeparatorChar(string[i]) ) str[i+1] = TA_SeparatorASCII(); else str[i+1] = string[i]; } } else { for( i=0; i < newStringLength; i++ ) str[i+1] = string[i]; } str[newStringLength+1] = '\0'; str[0] = 1; } return (TA_String *)str; }
static TA_String *stringValueAllocInternal( const char *string, unsigned int value ) { unsigned int size; char *str; size = ( strlen(string) + 2 + 8 ); str = (char *)TA_Malloc( size ); if( str != NULL ) { if( value > 0xFFFF ) value = 0xFFFF; sprintf( &str[1], "%08X%s", value, string ); str[0] = 1; } return (TA_String *)str; }
static TA_String *valueAllocInternal( unsigned long value ) { unsigned int size; char buffer[100]; char *str; buffer[0] = '\0'; sprintf(buffer, "%u", (unsigned int)value ); size = strlen(buffer)+2; str = (char *)TA_Malloc( size ); if( str != NULL ) { strcpy( &str[1], buffer ); str[0] = 1; } return (TA_String *)str; }
/* Like TA_FileSeqOpen, but work with a stream instead. */ TA_RetCode TA_FileSeqOpenFromStream( TA_Stream *stream, TA_FileHandle **handle ) { TA_PROLOG TA_FileHandlePriv *fileHandlePriv; TA_TRACE_BEGIN( TA_FileSeqOpen ); TA_ASSERT( stream != NULL ); TA_ASSERT( handle != NULL ); /* Allocate the private file handle. */ fileHandlePriv = (TA_FileHandlePriv *)TA_Malloc( sizeof( TA_FileHandlePriv ) ); if( !fileHandlePriv ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } memset( fileHandlePriv, 0, sizeof( TA_FileHandlePriv ) ); /* There is NO file... */ #if defined( USE_WIN32_API ) fileHandlePriv->handle = INVALID_HANDLE_VALUE; #endif #if defined( USE_OSLAYER ) fileHandlePriv->handle = (FILE *)NULL; #endif /* ... use a stream instead. */ fileHandlePriv->stream = stream; fileHandlePriv->streamAccess = TA_StreamAccessAlloc( stream ); if( !fileHandlePriv->streamAccess ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Success! Return the info to the caller. */ *handle = (TA_FileHandle *)fileHandlePriv; TA_TRACE_RETURN( TA_SUCCESS ); }
static TA_RetCode TA_SystemGlobalInit( void **globalToAlloc ) { TA_PROLOG TA_RetCode retCode; TA_SystemGlobal *global; TA_TRACE_BEGIN( TA_SystemGlobalInit ); TA_ASSERT( globalToAlloc != NULL ); *globalToAlloc = NULL; global = (TA_SystemGlobal *)TA_Malloc( sizeof( TA_SystemGlobal ) ); if( !global ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } memset( global, 0, sizeof( TA_SystemGlobal ) ); retCode = TA_StringCacheAlloc( &global->dirnameCache ); if( retCode != TA_SUCCESS ) { TA_Free( global ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } retCode = TA_StringCacheAlloc( &global->filenameCache ); if( retCode != TA_SUCCESS ) { TA_StringCacheFree( global->dirnameCache ); TA_Free( global ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Success! Return the global to the caller. */ *globalToAlloc = global; TA_TRACE_RETURN( TA_SUCCESS ); }
static TA_RetCode TA_SetReceiveTimeout( DWORD newTimeout, /* milliseconds */ HINTERNET hWebPage ) { BOOL status; /* Set the timeout who is going to affect calls to * InternetReadFile when retreiving data. */ #if 0 /* Code for debugging */ unsigned char *optionString; DWORD optionStringLength = 0; DWORD receiveTimeout; status = InternetQueryOption( hWebPage, INTERNET_OPTION_RECEIVE_TIMEOUT, NULL, &optionStringLength ); optionString = TA_Malloc( optionStringLength + 1); optionString[optionStringLength] = 0; InternetQueryOption( hWebPage, INTERNET_OPTION_RECEIVE_TIMEOUT, &receiveTimeout, &optionStringLength ); /*printf("GET INTERNET_OPTION_RECEIVE_TIMEOUT = %u\n", receiveTimeout );*/ TA_Free( (void *) optionString ); #endif status = InternetSetOption( hWebPage, INTERNET_OPTION_RECEIVE_TIMEOUT, &newTimeout, sizeof(DWORD) ); if( !status ) return TA_INTERNET_SET_RX_TIMEOUT_FAILED; return TA_SUCCESS; }
static TA_RetCode TA_TraceGlobalInit( void **globalToAlloc ) { TA_TraceGlobal *global; #if !defined( TA_SINGLE_THREAD ) TA_RetCode retCode; #endif if( !globalToAlloc ) return TA_BAD_PARAM; *globalToAlloc = NULL; global = TA_Malloc( sizeof( TA_TraceGlobal ) ); if( !global ) return TA_ALLOC_ERR; memset( global, 0, sizeof( TA_TraceGlobal ) ); #if !defined( TA_SINGLE_THREAD ) /* Initialize the mutexes in a non-block state. */ retCode = TA_SemaInit( &global->callSema, 1 ); if( retCode != TA_SUCCESS ) { TA_Free( global ); return retCode; } retCode = TA_SemaInit( &global->fatalSema, 1 ); if( retCode != TA_SUCCESS ) { TA_SemaDestroy( &global->callSema ); TA_Free( global ); return retCode; } #endif #ifdef TA_DEBUG #if defined( TA_SINGLE_THREAD ) /* When single threaded, maintain a calling stack. */ global->callStack = TA_ListAlloc(); if( !global->callStack ) { TA_Free( global ); return TA_ALLOC_ERR; } #endif /* All function call and checkpoint are maintained in a dictionary. */ global->functionCalled = TA_DictAlloc( TA_DICT_KEY_ONE_STRING, freeTracePosition ); if( !global->functionCalled ) { #if !defined( TA_SINGLE_THREAD ) TA_SemaDestroy( &global->callSema ); TA_SemaDestroy( &global->fatalSema ); #else TA_ListFree( global->callStack ); #endif TA_Free( global ); return TA_ALLOC_ERR; } #endif /* Success, return the allocated memory to the caller. */ *globalToAlloc = global; return TA_SUCCESS; }
TA_RetCode TA_ParamHolderAlloc( const TA_FuncHandle *handle, TA_ParamHolder **allocatedParams ) { TA_FuncDef *funcDef; unsigned int allocSize, i; TA_ParamHolderInput *input; TA_ParamHolderOptInput *optInput; TA_ParamHolderOutput *output; const TA_FuncInfo *funcInfo; TA_ParamHolder *newParams; TA_ParamHolderPriv *newParamsPriv; const TA_InputParameterInfo **inputInfo; const TA_OptInputParameterInfo **optInputInfo; const TA_OutputParameterInfo **outputInfo; /* Validate the parameters. */ if( !handle || !allocatedParams) { return TA_BAD_PARAM; } /* Validate that this is a valid funcHandle. */ funcDef = (TA_FuncDef *)handle; if( funcDef->magicNumber != TA_FUNC_DEF_MAGIC_NB ) { *allocatedParams = NULL; return TA_INVALID_HANDLE; } /* Get the TA_FuncInfo. */ funcInfo = funcDef->funcInfo; if( !funcInfo ) return TA_INVALID_HANDLE; /* Allocate the TA_ParamHolder. */ newParams = (TA_ParamHolder *)TA_Malloc( sizeof(TA_ParamHolder) + sizeof(TA_ParamHolderPriv)); if( !newParams ) { *allocatedParams = NULL; return TA_ALLOC_ERR; } memset( newParams, 0, sizeof(TA_ParamHolder) + sizeof(TA_ParamHolderPriv) ); newParamsPriv = (TA_ParamHolderPriv *)(((char *)newParams)+sizeof(TA_ParamHolder)); newParamsPriv->magicNumber = TA_PARAM_HOLDER_PRIV_MAGIC_NB; newParams->hiddenData = newParamsPriv; /* From this point, TA_ParamHolderFree can be safely called. */ /* Allocate the array of structure holding the info * for each parameter. */ if( funcInfo->nbInput == 0 ) return TA_INTERNAL_ERROR(2); allocSize = (funcInfo->nbInput) * sizeof(TA_ParamHolderInput); input = (TA_ParamHolderInput *)TA_Malloc( allocSize ); if( !input ) { TA_ParamHolderFree( newParams ); *allocatedParams = NULL; return TA_ALLOC_ERR; } memset( input, 0, allocSize ); newParamsPriv->in = input; if( funcInfo->nbOptInput == 0 ) optInput = NULL; else { allocSize = (funcInfo->nbOptInput) * sizeof(TA_ParamHolderOptInput); optInput = (TA_ParamHolderOptInput *)TA_Malloc( allocSize ); if( !optInput ) { TA_ParamHolderFree( newParams ); *allocatedParams = NULL; return TA_ALLOC_ERR; } memset( optInput, 0, allocSize ); } newParamsPriv->optIn = optInput; allocSize = (funcInfo->nbOutput) * sizeof(TA_ParamHolderOutput); output = (TA_ParamHolderOutput *)TA_Malloc( allocSize ); if( !output ) { TA_ParamHolderFree( newParams ); *allocatedParams = NULL; return TA_ALLOC_ERR; } memset( output, 0, allocSize ); newParamsPriv->out = output; newParamsPriv->funcInfo = funcInfo; inputInfo = (const TA_InputParameterInfo **)funcDef->input; optInputInfo = (const TA_OptInputParameterInfo **)funcDef->optInput; outputInfo = (const TA_OutputParameterInfo **)funcDef->output; for( i=0; i < funcInfo->nbInput; i++ ) { input[i].inputInfo = inputInfo[i]; newParamsPriv->inBitmap <<= 1; newParamsPriv->inBitmap |= 1; } for( i=0; i < funcInfo->nbOptInput; i++ ) { optInput[i].optInputInfo = optInputInfo[i]; if( optInput[i].optInputInfo->type == TA_OptInput_RealRange ) optInput[i].data.optInReal = optInputInfo[i]->defaultValue; else optInput[i].data.optInInteger = (TA_Integer)optInputInfo[i]->defaultValue; } for( i=0; i < funcInfo->nbOutput; i++ ) { output[i].outputInfo = outputInfo[i]; newParamsPriv->outBitmap <<= 1; newParamsPriv->outBitmap |= 1; } /* Succcess, return the result to the caller. */ *allocatedParams = newParams; return TA_SUCCESS; }
TA_RetCode TA_FuncTableAlloc( const char *group, TA_StringTable **table ) { TA_RetCode retCode; unsigned int i; TA_StringTable *stringTable; unsigned int groupId; /* TA_GroupId */ unsigned int groupSize; const char *stringPtr; TA_StringTablePriv *stringTablePriv; if( (group == NULL) || (table == NULL ) ) { return TA_BAD_PARAM; } *table = NULL; stringPtr = NULL; /* Get information on the group. */ retCode = getGroupId( group, &groupId ); if( retCode != TA_SUCCESS ) { return retCode; } retCode = getGroupSize( (TA_GroupId)groupId, &groupSize ); if( retCode != TA_SUCCESS ) { return retCode; } /* Allocate the table. */ stringTable = (TA_StringTable *)TA_Malloc( sizeof(TA_StringTable) + sizeof(TA_StringTablePriv) ); if( !stringTable ) { *table = NULL; return TA_ALLOC_ERR; } memset( stringTable, 0, sizeof(TA_StringTable) + sizeof(TA_StringTablePriv) ); stringTablePriv = (TA_StringTablePriv *)(((char *)stringTable)+sizeof(TA_StringTable)); stringTablePriv->magicNumber = TA_STRING_TABLE_FUNC_MAGIC_NB; stringTable->hiddenData = stringTablePriv; /* From this point, TA_FuncTableFree can be safely called. */ stringTable->size = groupSize; if( groupSize != 0 ) { stringTable->string = (const char **)TA_Malloc( (stringTable->size) * sizeof(const char *) ); if( stringTable->string == NULL ) { *table = NULL; TA_FuncTableFree( stringTable ); return TA_ALLOC_ERR; } memset( (void *)stringTable->string, 0, (stringTable->size) * sizeof(const char *) ); for( i=0; i < stringTable->size; i++ ) { retCode = getFuncNameByIdx( (TA_GroupId)groupId, i, &stringPtr ); if( retCode != TA_SUCCESS ) { *table = NULL; TA_FuncTableFree( stringTable ); return TA_ALLOC_ERR; } (stringTable->string)[i] = stringPtr; } } /* Return the table to the caller. */ *table = stringTable; return TA_SUCCESS; }
TA_RetCode TA_ReadOp_Do( TA_FileHandle *fileHandle, const TA_ReadOpInfo *readOpInfo, TA_Period period, const TA_Timestamp *start, const TA_Timestamp *end, unsigned int minimumNbBar, TA_Field fieldToAlloc, TA_ParamForAddData *paramForAddData, unsigned int *nbBarAdded ) { TA_PROLOG TA_RetCode retCode; TA_EstimateInfo estimationInfo; unsigned int nbElementToAllocate; unsigned int memoryNeeded; /* Boolean */ unsigned int timeNeeded; /* Boolean */ TA_Real *arrayReal[TA_REAL_ARRAY_SIZE]; TA_Integer *arrayInteger[TA_INTEGER_ARRAY_SIZE]; TA_Timestamp *timestamp; TA_Real *openBeg, *highBeg, *lowBeg, *closeBeg; TA_Integer *volumeBeg, *openInterestBeg; TA_Timestamp *timestampBeg; TA_Timestamp tmpTimestamp; TA_ReadOp op; TA_Field fieldToProcess; unsigned int year, month, day, hour, min, sec; TA_Integer curOp; unsigned int nbTotalByteDone, nbTotalBarDone; unsigned int nbBarAddedInTheBlock; char monthChar[4]; char cnvtArray[CNVT_ARRAY_SIZE]; unsigned int cnvtArrayIdx; unsigned int nbByteToAllocReal; unsigned int nbByteToAllocInteger; unsigned int fileSize; unsigned int skipField; unsigned int lineToSkip; unsigned int nbByteRead; unsigned int nbLetter; TA_Real lastValidClose; const char *car; register TA_Real tmpReal; register TA_Integer tmpInt; register unsigned int tmpIdx; register unsigned int nbCharToRead; unsigned int lastOpFieldIncremented; TA_TRACE_BEGIN( TA_PriceBarRead ); /* Initialization of local variables. */ openBeg = highBeg = lowBeg = closeBeg = NULL; timestampBeg = NULL; volumeBeg = openInterestBeg = NULL; timestamp = NULL; retCode = TA_SUCCESS; lastValidClose = 0.0; fieldToProcess = readOpInfo->fieldProvided & fieldToAlloc; if( (fieldToProcess & fieldToAlloc) != fieldToAlloc ) { /* Nothing to read because not all the requested * fields are provided by this data source! */ return TA_SUCCESS; } /* Estimate the initial amount of memory to allocate. */ fileSize = TA_FileSize( fileHandle ); retCode = TA_EstimateAllocInit( start, end, period, minimumNbBar, 2048, &estimationInfo, &nbElementToAllocate ); if( retCode != TA_SUCCESS ) { TA_TRACE_RETURN( retCode ); } if( nbElementToAllocate == 0 ) { TA_TRACE_RETURN( TA_SUCCESS ); /* Nothing to read!? Just return... */ } memset( arrayInteger, 0, sizeof( arrayInteger ) ); memset( arrayReal, 0, sizeof( arrayReal ) ); /* Set the date/time pointers to where the information will be stored. */ arrayInteger[TA_HOUR_IDX] = (TA_Integer *)&hour; arrayInteger[TA_MIN_IDX] = (TA_Integer *)&min; arrayInteger[TA_SEC_IDX] = (TA_Integer *)&sec; arrayInteger[TA_MONTH_IDX] = (TA_Integer *)&month; arrayInteger[TA_YEAR_IDX] = (TA_Integer *)&year; arrayInteger[TA_DAY_IDX] = (TA_Integer *)&day; /* Set default time/date. */ year = 1900; month = day = 1; hour = 23; min = sec = 59; /* Check if the processing of the time will be needed. */ timeNeeded = isTimeNeeded( readOpInfo->arrayReadOp); /* 'car' always point to the character being currently handled. */ nbByteRead = 0; car = TA_FileSeqRead( fileHandle, &nbByteRead ); if( (car == NULL) || (nbByteRead == 0) ) return TA_SUCCESS; /* End of file! */ --nbByteRead; nbTotalByteDone = 0; nbTotalBarDone = 0; nbBarAddedInTheBlock = 0; memoryNeeded = 1; curOp = 0; skipField = 0; monthChar[3] = '\0'; /* When requested, skip header lines. */ lineToSkip = readOpInfo->nbHeaderLineToSkip; while( lineToSkip-- ) { while( *car != '\n' ) { GET_CHAR; if( car == NULL ) goto exit_loops; } } line_loop: /* Always jump here when end-of-line is found (EOL). */ /* If curOp != 0, the last operations are canceled. */ REVERT_OPERATIONS; curOp = 0; lastOpFieldIncremented = 0; /* Start over a new line. */ if( memoryNeeded ) { /* Allocate the memory. */ nbByteToAllocReal = nbElementToAllocate * sizeof( TA_Real ); nbByteToAllocInteger = nbElementToAllocate * sizeof( TA_Integer ); timestamp = (TA_Timestamp *)TA_Malloc( nbElementToAllocate * sizeof( TA_Timestamp ) ); timestampBeg = timestamp; if( !timestampBeg ) { retCode = TA_ALLOC_ERR; goto exit_loops; } #define TA_ALLOC_MEM(upperc,lowerc,typepar) \ { \ if( fieldToProcess & TA_##upperc ) \ { \ lowerc##Beg = (TA_##typepar *)TA_Malloc( nbByteToAlloc##typepar ); \ array##typepar[TA_##upperc##_IDX] = lowerc##Beg; \ if( !lowerc##Beg ) \ { \ retCode = TA_ALLOC_ERR; \ goto exit_loops; \ } \ } \ } TA_ALLOC_MEM( OPEN, open, Real ); TA_ALLOC_MEM( HIGH, high, Real ); TA_ALLOC_MEM( LOW, low, Real ); TA_ALLOC_MEM( CLOSE, close, Real ); TA_ALLOC_MEM( VOLUME, volume, Integer ); TA_ALLOC_MEM( OPENINTEREST, openInterest, Integer ); #undef TA_ALLOC_MEM memoryNeeded = 0; } op_loop: /* Jump here when ready to proceed with the next command. */ op = readOpInfo->arrayReadOp[curOp]; if( !(op&TA_CMD_READ_MONTH_CHAR) ) { /* Skip leading non-numeric character. */ SKIP_UNTIL_NUMERIC; } /* Shall we skip this field? */ if( TA_IS_SKIP_SET(op) ) { if( (op&(TA_CMD_READ_REAL|TA_CMD_READ_INTEGER)) == 0 ) { tmpInt = TA_GET_NB_NUMERIC(op); curOp++; while( tmpInt-- ) { GET_CHAR; CHECK_EOL_EOF; } } else { if( skipField == 0 ) { skipField = TA_GET_NB_NUMERIC(op); TA_ASSERT( skipField > 0 ); } if( --skipField == 0 ) curOp++; SKIP_NUMERIC; if( (op&TA_CMD_READ_REAL) && (*car == '.') ) { GET_CHAR; CHECK_EOL_EOF; SKIP_NUMERIC; } } } else { cnvtArrayIdx = 0; if( TA_IS_REAL_CMD(op) ) { /* Extract a numeric into cnvtArray. */ READ_IN_CNVT_ARRAY; /* This is a TA_Real. */ if( car && (*car == '.') ) { /* Read rest of the float after the '.' */ READ_IN_CNVT_ARRAY; } cnvtArray[cnvtArrayIdx] = '\0'; tmpReal = atof( &cnvtArray[0] ); /* Write the TA_Real in memory. */ tmpIdx = TA_GET_IDX(op); TA_ASSERT( tmpIdx < TA_REAL_ARRAY_SIZE ); TA_ASSERT( arrayReal[tmpIdx] != NULL ); if( tmpReal != 0.0 ) { *(arrayReal[tmpIdx]) = tmpReal; if( tmpIdx == TA_CLOSE_IDX ) lastValidClose = tmpReal; } else if( TA_IS_REPLACE_ZERO(op) ) { /* Replace this zero value with the last known close. * If there is no previous close, this line is ignored. */ if( lastValidClose != 0.0 ) *(arrayReal[tmpIdx]) = lastValidClose; else { SKIP_LINE; } } else { /* Zero are not expected, consider this as a failure * and ignore all further data from this file. */ retCode = TA_PRICE_BAR_CONTAINS_ZERO; goto exit_loops; } arrayReal[tmpIdx]++; curOp++; } else { /* This is a TA_Integer. */ if( !(op&TA_CMD_READ_MONTH_CHAR) ) { nbCharToRead = TA_GET_NB_NUMERIC(op); if( nbCharToRead ) { READ_N_CHAR_IN_CNVT_ARRAY(nbCharToRead); } else { READ_IN_CNVT_ARRAY; } cnvtArray[cnvtArrayIdx] = '\0'; tmpInt = atoi( &cnvtArray[0] ); } else { /* Try to find a 3 letters month string. * Translate it to a [1..12] integer. */ nbLetter = 1; do { CHECK_EOL_EOF; monthChar[nbLetter] = (char)toupper(*car); GET_CHAR; nbLetter++; } while( nbLetter != 3 ); do { CHECK_EOL_EOF; monthChar[0] = monthChar[1]; monthChar[1] = monthChar[2]; monthChar[2] = (char)toupper(*car); if( strncmp("JAN",monthChar,3) == 0 ) tmpInt = 1; else if( strncmp("FEB",monthChar,3) == 0 ) tmpInt = 2; else if( strncmp("MAR",monthChar,3) == 0 ) tmpInt = 3; else if( strncmp("APR",monthChar,3) == 0 ) tmpInt = 4; else if( strncmp("MAY",monthChar,3) == 0 ) tmpInt = 5; else if( strncmp("JUN",monthChar,3) == 0 ) tmpInt = 6; else if( strncmp("JUL",monthChar,3) == 0 ) tmpInt = 7; else if( strncmp("AUG",monthChar,3) == 0 ) tmpInt = 8; else if( strncmp("SEP",monthChar,3) == 0 ) tmpInt = 9; else if( strncmp("OCT",monthChar,3) == 0 ) tmpInt = 10; else if( strncmp("NOV",monthChar,3) == 0 ) tmpInt = 11; else if( strncmp("DEC",monthChar,3) == 0 ) tmpInt = 12; else tmpInt = 0; GET_CHAR; } while( tmpInt == 0 ); } /* Write the TA_Integer in memory. */ tmpIdx = TA_GET_IDX(op); TA_ASSERT( tmpIdx < TA_INTEGER_ARRAY_SIZE ); TA_ASSERT( arrayInteger[tmpIdx] != NULL ); *(arrayInteger[tmpIdx]) = tmpInt; if( tmpIdx > TA_YEAR_IDX ) arrayInteger[tmpIdx]++; curOp++; if( TA_IS_TIMESTAMP_COMPLETE(op) ) { /* Build the timestamp. */ retCode = TA_SetDate( year, month, day, &tmpTimestamp ); if( retCode != TA_SUCCESS ) goto exit_loops; /* Invalid date */ if( !timeNeeded ) { /* Ignore time in comparison and use default to build the price bar. */ tmpTimestamp.time = 23595900; /* Default EOD time */ if( start && (tmpTimestamp.date < start->date) ) { /* This price bar is not needed, jump to the next line. */ retCode = TA_SUCCESS; SKIP_LINE; } if( end && (tmpTimestamp.date > end->date) ) { /* This price bar is beyond the upper limit, just exit. */ goto exit_loops; } } else { retCode = TA_SetTime( hour, min, sec, &tmpTimestamp ); if( retCode != TA_SUCCESS ) goto exit_loops; /* Invalid time */ if( start && TA_TimestampLess(&tmpTimestamp,start) ) { /* This price bar is not needed, jump to the next line. */ retCode = TA_SUCCESS; SKIP_LINE; } if( end && TA_TimestampGreater(&tmpTimestamp, end) ) { /* This price bar is beyond the upper limit, just exit. */ goto exit_loops; } } /* Write the timestamp in memory. */ *timestamp = tmpTimestamp; } } if( TA_IS_READ_STOP_FLAG_SET(op) ) { #ifdef DEBUG_PRINTF printf( "(%d%d%d,%e,%e,%e,%e,%d)\n", timestampBeg?TA_GetYear(×tampBeg[nbBarAddedInTheBlock]):0, timestampBeg?TA_GetMonth(×tampBeg[nbBarAddedInTheBlock]):0, timestampBeg?TA_GetDay(×tampBeg[nbBarAddedInTheBlock]):0, openBeg?openBeg[nbBarAddedInTheBlock]:0.0, highBeg?highBeg[nbBarAddedInTheBlock]:0.0, lowBeg?lowBeg[nbBarAddedInTheBlock]:0.0, closeBeg?closeBeg[nbBarAddedInTheBlock]:0.0, volumeBeg?volumeBeg[nbBarAddedInTheBlock]:0 ); #endif /* At this point, the price bar is completely written in memory. */ timestamp++; curOp = 0; nbBarAddedInTheBlock++; if( nbBarAddedInTheBlock < nbElementToAllocate ) { /* Go to next line. */ SKIP_LINE; } else { /* There is not enough memory for another bar, so re-allocate * some more. */ retCode = TA_HistoryAddData( paramForAddData, nbBarAddedInTheBlock, period, timestampBeg, openBeg, highBeg, lowBeg, closeBeg, volumeBeg, openInterestBeg ); /* TA_HistoryAddData is ALWAYS the owner of these memory block * when called. So we set these to NULL to make sure there will * be no attempt to free these from this function. */ openBeg = highBeg = lowBeg = closeBeg = NULL; timestampBeg = NULL; volumeBeg = openInterestBeg = NULL; nbTotalBarDone += nbBarAddedInTheBlock; nbBarAddedInTheBlock = 0; if( retCode != TA_SUCCESS ) goto exit_loops; retCode = TA_EstimateAllocNext( &estimationInfo, &nbElementToAllocate ); if( retCode != TA_SUCCESS ) goto exit_loops; memoryNeeded = 1; SKIP_LINE; } } else { /* Make sure we did not hit an EOL or EOF prematurly, if yes, * this line will be silently ignored. */ CHECK_EOL_EOF; } } goto op_loop; exit_loops: /* Jump here when the end-of-file is hit (or an error occured) */ /* On succesful exit, process possibly remaining data. */ if( (retCode == TA_SUCCESS) && (nbBarAddedInTheBlock != 0) ) { retCode = TA_HistoryAddData( paramForAddData, nbBarAddedInTheBlock, period, timestampBeg, openBeg, highBeg, lowBeg, closeBeg, volumeBeg, openInterestBeg ); openBeg = highBeg = lowBeg = closeBeg = NULL; timestampBeg = NULL; volumeBeg = openInterestBeg = NULL; nbTotalBarDone += nbBarAddedInTheBlock; } /* ALWAYS verify if locally allocated memory needs to be freed. ALWAYS. */ FREE_IF_NOT_NULL( openBeg ); FREE_IF_NOT_NULL( highBeg ); FREE_IF_NOT_NULL( lowBeg ); FREE_IF_NOT_NULL( closeBeg ); FREE_IF_NOT_NULL( volumeBeg ); FREE_IF_NOT_NULL( openInterestBeg ); FREE_IF_NOT_NULL( timestampBeg ); /* An indication that no more data needs to be provided is not a failure. */ if( retCode == TA_ENOUGH_DATA ) retCode = TA_SUCCESS; /* Return the number of added price bar to the caller. */ if( nbBarAdded ) *nbBarAdded = nbTotalBarDone; TA_TRACE_RETURN( retCode ); }
/**** 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 ); }
TA_RetCode TA_FileSeqOpen( const char *path, TA_FileHandle **handle ) { TA_PROLOG TA_FileHandlePriv *fileHandlePriv; TA_TRACE_BEGIN( TA_FileSeqOpen ); TA_ASSERT( path != NULL ); TA_ASSERT( handle != NULL ); fileHandlePriv = (TA_FileHandlePriv *)TA_Malloc( sizeof( TA_FileHandlePriv ) ); if( !fileHandlePriv ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } memset( fileHandlePriv, 0, sizeof( TA_FileHandlePriv ) ); #if defined( USE_WIN32_API ) fileHandlePriv->handle = CreateFile( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if( fileHandlePriv->handle == INVALID_HANDLE_VALUE ) { TA_FileSeqClose( (TA_FileHandle *)fileHandlePriv ); TA_TRACE_RETURN( TA_ACCESS_FAILED ); } #else /* For all non-win32 platform, use standard ANSI C I/O */ fileHandlePriv->handle = fopen( path, "rb" ); if( fileHandlePriv->handle == 0 ) { TA_FileSeqClose( (TA_FileHandle *)fileHandlePriv ); TA_TRACE_RETURN( TA_ACCESS_FAILED ); } #endif /* Allocate buffer memory. */ #if defined( USE_WIN32_API ) fileHandlePriv->allocBuffer = allocDiskBuffer( path, &fileHandlePriv->allocBufferSize ); if( !fileHandlePriv->allocBuffer || (fileHandlePriv->allocBufferSize == 0) ) { TA_FileSeqClose( (TA_FileHandle *)fileHandlePriv ); TA_TRACE_RETURN( TA_ACCESS_FAILED ); } #else fileHandlePriv->allocBuffer = TA_Malloc( FILE_BUFFER_SIZE ); fileHandlePriv->allocBufferSize = FILE_BUFFER_SIZE; if( !fileHandlePriv->allocBuffer ) { TA_FileSeqClose( (TA_FileHandle *)fileHandlePriv ); TA_TRACE_RETURN( TA_ACCESS_FAILED ); } #endif /* Keep a ptr on the path. */ fileHandlePriv->path = path; /* Success! Return the info to the caller. */ *handle = (TA_FileHandle *)fileHandlePriv; TA_TRACE_RETURN( TA_SUCCESS ); }
TA_RetCode TA_DirectoryAlloc( const char *path, TA_Directory **directory ) { #if defined( USE_WIN32_API ) HANDLE handle; WIN32_FIND_DATA data; DWORD win32Error; #endif #if defined( USE_OSLAYER ) DIRST dirHandle; const char *filePattern; char *basePath; #endif unsigned pathLength; int findNextRetCode; TA_Directory *dir; TA_String *string; TA_RetCode retCode; TA_SystemGlobal *global; const char *entryName; unsigned int entryIsDirectory; *directory = NULL; if( (path == NULL) || (directory == NULL) ) return TA_BAD_PARAM; retCode = TA_GetGlobal( &TA_SystemGlobalControl, (void **)&global ); if( retCode != TA_SUCCESS ) return retCode; dir = (TA_Directory *)TA_Malloc( sizeof( TA_Directory ) ); if( dir == NULL ) return TA_ALLOC_ERR; dir->nbFile = 0; dir->nbDirectory = 0; dir->listOfFile = TA_ListAlloc(); dir->listOfDirectory = TA_ListAlloc(); if( (dir->listOfFile == NULL) || (dir->listOfDirectory == NULL) ) { TA_DirectoryFree( dir ); return TA_ALLOC_ERR; } /* Verify that the path is valid. */ pathLength = strlen( path ); if( (pathLength == 0) || (pathLength >= MAX_PATH) ) { TA_DirectoryFree( dir ); return TA_BAD_PARAM; } /* Now get the directory from the operating system. */ #if defined( USE_WIN32_API ) handle = FindFirstFile( path, &data ); if( handle == INVALID_HANDLE_VALUE ) { win32Error = GetLastError(); global->lastError = win32Error; if( (win32Error != ERROR_FILE_NOT_FOUND) && (win32Error != ERROR_PATH_NOT_FOUND) ) { TA_DirectoryFree( dir ); return TA_ACCESS_FAILED; } /* No files or directory... but still have to pass the result * to the caller. */ *directory = dir; return TA_SUCCESS; } entryName = data.cFileName; entryIsDirectory = data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; #endif #if defined( USE_OSLAYER ) /* Split the path into the basePath and the filePattern. */ basePath = TA_Malloc( pathLength+1 ); memcpy( basePath, path, pathLength+1 ); filePattern = split_path_and_file( basePath ); if( !filePattern ) { /* With no filePattern, no file can be found... * so return an empty directory to the caller. */ *directory = dir; TA_Free( basePath ); return TA_SUCCESS; } /* Look for last separetor. */ if( !open_dir(&dirHandle, basePath ) ) { /* Errors, or no files or no directory... but * still have to pass the result to the caller. */ TA_Free( basePath ); *directory = dir; return TA_SUCCESS; } entryName = dirHandle.file_name; entryIsDirectory = dirHandle.file_attrs & ATTR_SUBDIR; #endif do { #if defined( USE_OSLAYER ) if( file_matches( entryName, filePattern ) ) { #endif if( entryIsDirectory ) { if( entryName[0] != '.' ) { string = TA_StringAlloc( global->dirnameCache, entryName ); if( string == NULL ) { #if defined( USE_OSLAYER ) close_dir(&dirHandle); TA_Free( basePath ); #endif TA_DirectoryFree( dir ); return TA_ALLOC_ERR; } retCode = TA_ListAddTail( dir->listOfDirectory, (void *)string ); if( retCode != TA_SUCCESS ) { #if defined( USE_OSLAYER ) close_dir(&dirHandle); TA_Free( basePath ); #endif TA_DirectoryFree( dir ); return retCode; } dir->nbDirectory++; } } else { string = TA_StringAlloc( global->filenameCache, entryName ); if( string == NULL ) { #if defined( USE_OSLAYER ) close_dir(&dirHandle); TA_Free( basePath ); #endif TA_DirectoryFree( dir ); return TA_ALLOC_ERR; } retCode = TA_ListAddTail( dir->listOfFile, (void *)string ); if( retCode != TA_SUCCESS ) { #if defined( USE_OSLAYER ) close_dir(&dirHandle); TA_Free( basePath ); #endif TA_DirectoryFree( dir ); return retCode; } dir->nbFile++; } #if defined( USE_OSLAYER ) } #endif #if defined( USE_WIN32_API ) findNextRetCode = FindNextFile( handle, &data ); entryName = data.cFileName; entryIsDirectory = data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; #endif #if defined( USE_OSLAYER ) findNextRetCode = read_dir( &dirHandle ); entryName = dirHandle.file_name; entryIsDirectory = dirHandle.file_attrs & ATTR_SUBDIR; #endif } while( findNextRetCode == TRUE ); #if defined( USE_OSLAYER ) TA_Free( basePath ); if( !close_dir(&dirHandle) ) { TA_DirectoryFree( dir ); return TA_INTERNAL_ERROR(11); } #endif #if defined( USE_WIN32_API ) if( FindClose( handle ) != TRUE ) { global->lastError = GetLastError(); TA_DirectoryFree( dir ); return TA_INTERNAL_ERROR(12); } #endif /* Pass the result to the caller. */ *directory = dir; return TA_SUCCESS; }
/**** 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 )
/**** Local functions definitions. ****/ static TA_RetCode rangeTestFunction( TA_Integer startIdx, TA_Integer endIdx, TA_Real *outputBuffer, TA_Integer *outputBufferInt, TA_Integer *outBegIdx, TA_Integer *outNbElement, TA_Integer *lookback, void *opaqueData, unsigned int outputNb, unsigned int *isOutputInteger ) { TA_RetCode retCode; TA_RangeTestParam *testParam; double *dummyBuffer; (void)outputNb; (void)outputBufferInt; *isOutputInteger = 0; testParam = (TA_RangeTestParam *)opaqueData; /* Allocate a buffer for the output who is going * to be ignored (make it slightly larger to play * safe) */ dummyBuffer = TA_Malloc( sizeof(double) * (endIdx-startIdx+100) ); switch( testParam->test->theFunction ) { case TA_AROON_UP_TEST: retCode = TA_AROON( startIdx, endIdx, testParam->high, testParam->low, testParam->test->optInTimePeriod, outBegIdx, outNbElement, &dummyBuffer[20], outputBuffer ); *lookback = TA_AROON_Lookback( testParam->test->optInTimePeriod ); break; case TA_AROON_DOWN_TEST: retCode = TA_AROON( startIdx, endIdx, testParam->high, testParam->low, testParam->test->optInTimePeriod, outBegIdx, outNbElement, outputBuffer, &dummyBuffer[20] ); *lookback = TA_AROON_Lookback( testParam->test->optInTimePeriod ); break; case TA_AROONOSC_TEST: retCode = TA_AROONOSC( startIdx, endIdx, testParam->high, testParam->low, testParam->test->optInTimePeriod, outBegIdx, outNbElement, outputBuffer ); *lookback = TA_AROONOSC_Lookback( testParam->test->optInTimePeriod ); break; case TA_CORREL_TEST: retCode = TA_CORREL( startIdx, endIdx, testParam->high, testParam->low, testParam->test->optInTimePeriod, outBegIdx, outNbElement, outputBuffer ); *lookback = TA_CORREL_Lookback( testParam->test->optInTimePeriod ); break; default: retCode = TA_INTERNAL_ERROR(132); } TA_Free( dummyBuffer ); return retCode; }
/**** Local functions definitions. ****/ static TA_RetCode rangeTestFunction( TA_Libc *libHandle, TA_Integer startIdx, TA_Integer endIdx, TA_Real *outputBuffer, TA_Integer *outBegIdx, TA_Integer *outNbElement, TA_Integer *lookback, void *opaqueData, unsigned int outputNb ) { TA_RetCode retCode; TA_RangeTestParam *testParam; TA_Real *dummyOutput; testParam = (TA_RangeTestParam *)opaqueData; dummyOutput = TA_Malloc( libHandle, (endIdx-startIdx+1) * sizeof(TA_Real) ); if( outputNb == 0 ) { retCode = TA_STOCH( libHandle, startIdx, endIdx, testParam->high, testParam->low, testParam->close, testParam->test->optInKPeriod_0, testParam->test->optInKSlowPeriod_1, testParam->test->optInDPeriod_2, testParam->test->optInMethod_3, outBegIdx, outNbElement, outputBuffer, dummyOutput ); } else { retCode = TA_STOCH( libHandle, startIdx, endIdx, testParam->high, testParam->low, testParam->close, testParam->test->optInKPeriod_0, testParam->test->optInKSlowPeriod_1, testParam->test->optInDPeriod_2, testParam->test->optInMethod_3, outBegIdx, outNbElement, dummyOutput, outputBuffer ); } TA_Free( libHandle, dummyOutput ); *lookback = TA_STOCH_Lookback( testParam->test->optInKPeriod_0, testParam->test->optInKSlowPeriod_1, testParam->test->optInDPeriod_2, testParam->test->optInMethod_3 ); return retCode; }