Example #1
0
/**** Local functions definitions.     ****/
static void freePrivateHandle( TA_PrivateHandle *privData )
{
   TA_StringCache *stringCache;

   stringCache = TA_GetGlobalStringCache();

   if( stringCache )
   {
      if( privData->ta_sim_ref_cat )
         TA_StringFree( stringCache, privData->ta_sim_ref_cat );

      if( privData->ta_sim_mrg_cat )
         TA_StringFree( stringCache, privData->ta_sim_mrg_cat );

      if( privData->daily_ref_0 )
         TA_StringFree( stringCache, privData->daily_ref_0 );

      if( privData->intra_ref_0 )
         TA_StringFree( stringCache, privData->intra_ref_0 );

      if( privData->mrg_0 )
         TA_StringFree( stringCache, privData->mrg_0 );
   }

   FREE_IF_NOT_NULL( privData->mrgHistory.open );
   FREE_IF_NOT_NULL( privData->mrgHistory.high );
   FREE_IF_NOT_NULL( privData->mrgHistory.low );
   FREE_IF_NOT_NULL( privData->mrgHistory.close );
   FREE_IF_NOT_NULL( privData->mrgHistory.volume );
   FREE_IF_NOT_NULL( privData->mrgHistory.openInterest );
   FREE_IF_NOT_NULL( privData->mrgHistory.timestamp );

   TA_Free(  privData );    
}
Example #2
0
void TA_PrivTraceCheckpoint( const char *funcname,
                             const char *filename,
                             unsigned int lineNb )
{
   TA_TraceGlobal *global;
   TA_RetCode retCode;

   #if defined( TA_DEBUG )
      TA_String *key;
      TA_StringCache *stringCache;
   #endif

   /* Get access to the global. */
   retCode = TA_GetGlobal( &TA_TraceGlobalControl, (void **)&global );
   if( retCode != TA_SUCCESS )
      return;

   #if !defined( TA_DEBUG )
      internalCheckpoint( global, NULL, funcname, filename, lineNb );
   #else
      /* Build a unique string representing the position. */
      stringCache = TA_GetGlobalStringCache();
      key = TA_StringValueAlloc( stringCache, filename, lineNb );
      if( !key )
         return;

      internalCheckpoint( global, key, funcname, filename, lineNb );

      TA_StringFree( stringCache, key );
   #endif
}
Example #3
0
/* Allows to dynamically add a mini-driver. */
TA_RetCode TA_SQL_AddMinidriver( const char scheme[], const TA_SQL_Minidriver *minidriver )
{
   TA_PROLOG
   TA_String *schemeStr;
   TA_StringCache *cache;

   TA_RetCode retCode;

   TA_TRACE_BEGIN( TA_SQL_AddMinidriver );

   if( !minidriverDict )
   {
      minidriverDict = TA_DictAlloc( TA_DICT_KEY_ONE_STRING, NULL );
      if( !minidriverDict )
      {
         TA_TRACE_RETURN( TA_ALLOC_ERR );
      }
   }

   cache = TA_GetGlobalStringCache();
   TA_ASSERT( cache != NULL );
   schemeStr = TA_StringAlloc( cache, scheme );
   if( !schemeStr )
   {
      TA_DictFree(minidriverDict);
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   retCode = TA_DictAddPair_S( minidriverDict, schemeStr, (void *)minidriver );
   TA_StringFree( cache, schemeStr );

   TA_TRACE_RETURN( retCode );
}
Example #4
0
TA_RetCode TA_DictDeletePair_S( TA_Dict *dict, const char *key )
{
   TA_PrivDictInfo *theDict;
   TA_String *stringToDelete;
   void      *valueToDelete;
   dnode_t   *node;
   TA_Libc   *libHandle;
   dict_t    *kazlibDict;

   theDict = (TA_PrivDictInfo *)dict;

   if( (theDict == NULL) || (key == NULL) )
      return TA_BAD_PARAM;
   kazlibDict = &theDict->d;
   libHandle  = theDict->libHandle;

   /* Find the key-value pair. */
   node = dict_lookup( libHandle, kazlibDict, key );

   if( node )
   {
      /* Free the 'node', the 'key' string and the 'value'. */
      stringToDelete = TA_StringFromChar( dnode_getkey(node) );
      valueToDelete  = dnode_get(node);
      dict_delete_free( libHandle, kazlibDict, node );
      TA_StringFree( TA_GetGlobalStringCache( libHandle ), stringToDelete );
      if( theDict->freeValueFunc )
         theDict->freeValueFunc( libHandle, valueToDelete );
   }
   else
      return TA_KEY_NOT_FOUND;

   return TA_SUCCESS;
}
Example #5
0
TA_RetCode TA_StringCacheFree( TA_StringCache *stringCacheToFree )
{
   TA_StringCachePriv *stringCachePriv;
   unsigned int i;

   if( !stringCacheToFree )
      return TA_BAD_PARAM;

   stringCachePriv = (TA_StringCachePriv *)stringCacheToFree;

   for( i=0; i < NB_CACHE_ENTRY; i++ )
   {
      if( stringCachePriv->cache[i] != NULL )
         TA_StringFree( stringCacheToFree,
                        (TA_String *)stringCachePriv->cache[i] );
   }

   #if !defined( TA_SINGLE_THREAD )
      TA_SemaDestroy( &stringCachePriv->sema );
   #endif

   TA_Free(  stringCachePriv );

   return TA_SUCCESS;
}
Example #6
0
static TA_RetCode freeSymbolData( TA_Libc *libHandle, void *toBeFreed )
{
   TA_FileIndexSymbolData *symbolData;
   TA_StringCache *stringCache;

   stringCache = TA_GetGlobalStringCache( libHandle );

   symbolData = (TA_FileIndexSymbolData *)toBeFreed;

   if( !symbolData )
      return TA_ALLOC_ERR;

   /* Not yet im[plemented !!!
   if( symbolData->dataPerDate )
   {
      if( freeListAndElement( symbolData->dataPerDate, freeDataPerDate ) != TA_SUCCESS )
         return TA_ALLOC_ERR;
   }
   */

   if( symbolData->string )
      TA_StringFree( stringCache, symbolData->string );

   TA_Free( libHandle, symbolData );

   return TA_SUCCESS;
}
Example #7
0
static TA_RetCode freeFileIndexPriv( void *toBeFreed )
{
   TA_PROLOG;
   TA_FileIndexPriv *asciiFileData;
   TA_Libc          *libHandle;
   TA_StringCache   *stringCache;

   asciiFileData = (TA_FileIndexPriv *)toBeFreed;

   libHandle   = asciiFileData->libHandle;
   TA_TRACE_BEGIN( libHandle, freeFileIndexPriv );

   stringCache = TA_GetGlobalStringCache( libHandle );

   if( !asciiFileData )
   {
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   if( freeListAndElement( libHandle, asciiFileData->listLocationToken, freeTokenInfo ) != TA_SUCCESS )
   {
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   if( freeListAndElement( libHandle, asciiFileData->listCategory, freeCategoryData ) != TA_SUCCESS )
   {
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   if( asciiFileData->root )
      TA_FileIndexFreeValueTree( libHandle, asciiFileData->root );

   if( asciiFileData->scratchPad )
      TA_Free( libHandle, asciiFileData->scratchPad );

   if( asciiFileData->wildOneChar )
      TA_StringFree( stringCache, asciiFileData->wildOneChar );

   if( asciiFileData->wildZeroOrMoreChar )
      TA_StringFree( stringCache, asciiFileData->wildZeroOrMoreChar );

   if( asciiFileData->wildOneOrMoreChar )
      TA_StringFree( stringCache, asciiFileData->wildOneOrMoreChar );

   if( asciiFileData->initialCategoryString )
      TA_StringFree( stringCache, asciiFileData->initialCategoryString );

   if( asciiFileData->initialCategoryCountryString )
      TA_StringFree( stringCache, asciiFileData->initialCategoryCountryString );

   if( asciiFileData->initialCategoryExchangeString )
      TA_StringFree( stringCache, asciiFileData->initialCategoryExchangeString );

   if( asciiFileData->initialCategoryTypeString )
      TA_StringFree( stringCache, asciiFileData->initialCategoryTypeString );

   TA_Free( libHandle, asciiFileData );

   TA_TRACE_RETURN( TA_SUCCESS );
}
Example #8
0
TA_RetCode TA_DictFree( TA_Dict *dict )
{
   TA_PrivDictInfo *theDict;

   dnode_t   *node;
   dnode_t   *next;
   TA_String *stringToDelete;
   void      *valueToDelete;
   dict_t    *kazlibDict;
   TA_Libc   *libHandle;
   int        flags;

   theDict = (TA_PrivDictInfo *)dict;

   if( theDict == NULL )
      return TA_BAD_PARAM;

   kazlibDict = &theDict->d;
   libHandle  = theDict->libHandle;

   /* Delete all the key-value pair sequentially. */
   node = dict_first( libHandle, kazlibDict );

   while (node != NULL)
   {
      /* Get the next node. */
      next = dict_next( libHandle, kazlibDict, node );

      /* Free the 'node, the 'key' string and the 'value'. */
      flags = theDict->flags;
      valueToDelete  = dnode_get(node);
      if( flags & (TA_DICT_KEY_TWO_STRING|TA_DICT_KEY_ONE_STRING) )
      {
         stringToDelete = TA_StringFromChar(dnode_getkey(node));
         dict_delete_free( libHandle, kazlibDict, node );
         TA_StringFree( TA_GetGlobalStringCache( libHandle ), stringToDelete );
      }
      else
         dict_delete_free( libHandle, kazlibDict, node );

      if( flags & TA_DICT_KEY_TWO_STRING )
      {
         /* The value is a dictionary. Delete it. */
         TA_DictFree( (TA_Dict *)valueToDelete );
      }
      else if( theDict->freeValueFunc )
         theDict->freeValueFunc( libHandle, valueToDelete );

      node = next;
   }

   /* Free the TA_PrivDictInfo */
   TA_Free( libHandle, theDict );

   return TA_SUCCESS;
}
Example #9
0
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 );
}
Example #10
0
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 );
}
Example #11
0
/**** Local functions definitions.     ****/
static void freeTradeDictEntry( void *toBeFreed )
{
    TA_TradeDictEntry *dictEntry;
    TA_StringCache *stringCache;
    TA_String *catString;
    TA_String *symString;
    const char *tempCharPtr;

    dictEntry = (TA_TradeDictEntry *)toBeFreed;
    if( !dictEntry )
        return;

    tempCharPtr = dictEntry->id.catString;
    if( tempCharPtr )
    {
        catString = TA_StringFromChar(tempCharPtr);
        stringCache = TA_GetGlobalStringCache();
        TA_StringFree( stringCache, catString );

        tempCharPtr = dictEntry->id.symString;
        if( tempCharPtr )
        {
            symString = TA_StringFromChar(tempCharPtr);
            TA_StringFree( stringCache, symString );
        }
    }
    else
    {
        tempCharPtr = dictEntry->id.symString;
        if( tempCharPtr )
        {
            symString = TA_StringFromChar(tempCharPtr);
            stringCache = TA_GetGlobalStringCache();
            TA_StringFree( stringCache, symString );
        }
    }

    TA_Free( dictEntry );
}
Example #12
0
/**** Local functions definitions.     ****/
static void stringListFree( TA_StringCache *stringCache, TA_List *list )
{
   TA_String *node;

   if( list == NULL )
      return;

   while( (node = (TA_String *)TA_ListRemoveHead( list )) != NULL )
   {
      TA_StringFree( stringCache, node );
   }

   TA_ListFree( list );
}
Example #13
0
TA_RetCode TA_FileIndexAddTreeValue( TA_FileIndexPriv *data,
                                     TA_String *string,
                                     TA_ValueTreeNode **added )
{
   TA_PROLOG;
   TA_ValueTreeNode *node;
   unsigned int allocateEmptyString;
   TA_Libc *libHandle;
   TA_StringCache *stringCache;

   libHandle = data->libHandle;
   TA_TRACE_BEGIN( libHandle, TA_FileIndexAddTreeValue );

   stringCache = TA_GetGlobalStringCache( libHandle );

   allocateEmptyString = 0;
   TA_ASSERT( libHandle, data != NULL );

   if( added )
      *added = NULL;

   if( !string )
   {
      string = TA_StringAlloc( stringCache, "" );
      if( !string )
      {
         TA_TRACE_RETURN( TA_ALLOC_ERR );
      }
      allocateEmptyString = 1;
   }

   /* Alloc the TA_ValueTreeNode */
   node = allocTreeNode( libHandle, data->currentNode, string );

   if( allocateEmptyString )
      TA_StringFree( stringCache, string );

   if( !node )
   {
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   data->currentNode = node;

   if( added )
      *added = node;

   TA_TRACE_RETURN( TA_SUCCESS );
}
Example #14
0
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;
}
Example #15
0
static TA_RetCode freeTokenInfo( TA_Libc *libHandle, void *toBeFreed )
{
   TA_TokenInfo *info;
   TA_StringCache *stringCache;

   stringCache = TA_GetGlobalStringCache( libHandle );

   info = (TA_TokenInfo *)toBeFreed;

   if( !info )
      return TA_ALLOC_ERR;

   if( info->value )
      TA_StringFree( stringCache, info->value );

   TA_Free( libHandle, info );

   return TA_SUCCESS;
}
Example #16
0
TA_RetCode TA_DictDeletePair_S2( TA_Dict *dict, const char *key1, const char *key2 )
{
   TA_PrivDictInfo *theDict;
   TA_String *stringToDelete;
   dnode_t   *node;
   TA_Dict   *subDict;
   TA_RetCode retCode;
   TA_Libc   *libHandle;
   dict_t    *kazlibDict;

   theDict = (TA_PrivDictInfo *)dict;

   if( (theDict == NULL)    ||
       (key1 == NULL)       || 
       (key2 == NULL))
      return TA_BAD_PARAM;
   kazlibDict = &theDict->d;
   libHandle  = theDict->libHandle;

   /* Find the dictionary for this 'key1'. */
   node = dict_lookup( libHandle, kazlibDict, key1 );

   if( !node )
      return TA_KEY_NOT_FOUND;

   subDict = (TA_Dict *)dnode_get(node);

   retCode = TA_DictDeletePair_S( subDict, key2 );

   /* Delete the dictionary if it is empty. */
   if( (retCode == TA_SUCCESS) && (TA_DictSize(subDict) == 0) )
   {
      TA_DictFree( subDict );
      /* Free the 'node' and the 'key1' string. */
      stringToDelete = TA_StringFromChar( dnode_getkey(node) );
      dict_delete_free( theDict->libHandle, kazlibDict, node );
      TA_StringFree( TA_GetGlobalStringCache( libHandle ), stringToDelete );
   }
   return TA_SUCCESS;
}
Example #17
0
static TA_RetCode freeCategoryData( TA_Libc *libHandle, void *toBeFreed )
{
   TA_FileIndexCategoryData *categoryData;
   TA_StringCache *stringCache;

   stringCache = TA_GetGlobalStringCache( libHandle );

   categoryData = (TA_FileIndexCategoryData *)toBeFreed;

   if( !categoryData )
      return TA_ALLOC_ERR;

   if( categoryData->string )
      TA_StringFree( stringCache, categoryData->string );

   if( freeListAndElement( libHandle, categoryData->listSymbol, freeSymbolData ) != TA_SUCCESS )
      return TA_ALLOC_ERR;

   TA_Free( libHandle, categoryData );

   return TA_SUCCESS;
}
Example #18
0
static TA_RetCode internalMarketPageFree( TA_YahooMarketPage *marketPage )
{
   TA_Libc *libHandle;
   TA_StringCache *stringCache;

   if( marketPage )
   {
      libHandle = marketPage->libHandle;

      if( marketPage->magicNb != TA_MARKET_PAGE_MAGIC_NB )
         return TA_BAD_OBJECT;

      if( marketPage->name )
      {
         stringCache = TA_GetGlobalStringCache( libHandle );
         TA_StringFree( stringCache, marketPage->name );
      }

      TA_Free( libHandle, marketPage );
   }

   return TA_SUCCESS;
}
Example #19
0
TA_RetCode TA_FileIndexFreeValueTree( TA_Libc *libHandle,
                                      TA_ValueTreeNode *fromNode )
{
   TA_PROLOG;
   TA_RetCode retCode;
   TA_ValueTreeNode *tmp;
   TA_StringCache *stringCache;

   TA_TRACE_BEGIN( libHandle, TA_FileIndexFreeValueTree );

   TA_ASSERT( libHandle, fromNode != NULL );

   stringCache = TA_GetGlobalStringCache( libHandle );

   /* Remove itself from parent->child list if parent still around. */
   if( fromNode->parent )
   {
      tmp = ((TA_ValueTreeNode *)fromNode->parent);
      if( tmp->child )
         TA_ListRemoveEntry( tmp->child, (void *)fromNode );
   }

   if( fromNode->string )
      TA_StringFree( stringCache, fromNode->string );

   /* Deletes all childs. */
   if( fromNode->child )
   {
      retCode = freeListAndElement( libHandle, fromNode->child, freeTreeNode );
      if( retCode != TA_SUCCESS )
         TA_TRACE_RETURN( retCode );
   }

   TA_Free( libHandle, fromNode );

   TA_TRACE_RETURN( TA_SUCCESS );
}
Example #20
0
/* Allows to change the value associated to a TA_ValueTreeNode. */
TA_RetCode TA_FileIndexChangeValueTreeNodeValue( TA_Libc *libHandle,
                                                 TA_ValueTreeNode *nodeToChange,
                                                 TA_String *newValue )
{
   TA_PROLOG;
   TA_String *dup;
   TA_StringCache *stringCache;

   TA_TRACE_BEGIN( libHandle, TA_FileIndexChangeValueTreeNodeValue );

   stringCache = TA_GetGlobalStringCache( libHandle );

   if( !nodeToChange )
   {
      TA_TRACE_RETURN( TA_BAD_PARAM );
   }

   if( nodeToChange->string )
      TA_StringFree( stringCache, nodeToChange->string );

   if( !newValue )
      nodeToChange->string = NULL;
   else
   {
      dup = TA_StringDup( stringCache, newValue );
      if( !dup )
      {
         nodeToChange->string = NULL;
         TA_TRACE_RETURN( TA_ALLOC_ERR );
      }
      else
         nodeToChange->string = dup;
   }

   TA_TRACE_RETURN( TA_SUCCESS );
}
Example #21
0
TA_RetCode TA_AllocStringFromYahooName( TA_Libc *libHandle,
                                        TA_DecodingParam *marketDecodingParam,
                                        const char *yahooSymbol,
                                        TA_String **allocatedCategoryName,
                                        TA_String **allocatedSymbolName,
                                        unsigned int allowOnlineProcessing )
{
   TA_PROLOG;

   TA_RetCode retCode;

   const char   *symbol;
   unsigned int symbolLength;
   const char   *ext;

   const char *countryAbbrev;
   const char *exchangeString;
   const char *typeString;

   unsigned int i;
   char *tempBuffer;
   TA_String *allocCategory;
   TA_String *allocSymbol;
   TA_StringCache *stringCache;
   TA_CountryId countryId;

   TA_YahooMarketPage *allocatedMarketPage;

   TA_TRACE_BEGIN( libHandle, TA_AllocStringFromYahooName );

   /* Validate parameter */
   if( !libHandle || !marketDecodingParam || !yahooSymbol ||
       !allocatedCategoryName || !allocatedSymbolName )
   {
      TA_TRACE_RETURN( TA_BAD_PARAM );
   }

   stringCache = TA_GetGlobalStringCache( libHandle );

   /* Set a pointer on where the symbol start. */
   symbol = yahooSymbol;
  
   /* Size of the symbol. */
   symbolLength = getstrfldlen( symbol, 0, 0, "." );
   if( symbolLength < 2 )
   {
      TA_TRACE_RETURN( TA_BAD_PARAM );
   }
   else
      symbolLength--;

   /* The 3 strings forming the final Category string. */
   countryAbbrev  = NULL;
   exchangeString = NULL;
   typeString     = NULL;

   /* Identify if there is an extension. */
   ext = strchr( symbol, '.' );
   if( ext )
   {
      ext++;
      if( *ext == '\0' )
         ext = NULL;
   }

   /* If ext != NULL, ext points on first char of the extension. */
   
   if( ext )
   {
      /* Identify known USA/CAN extension. */
      for( i=0; i < NB_YAHOO_EXCHANGE_EXTENSION; i++ )
      {
         if( lexcmp( TA_YahooExtensionTable[i].extension, ext ) == 0 )
         {
            countryId = TA_YahooExtensionTable[i].countryId;           
            countryAbbrev  = TA_CountryIdToAbbrev( countryId );
            exchangeString = TA_YahooExtensionTable[i].exchange;
            typeString     = TA_YahooExtensionTable[i].type;
            break; /* Exit the loop */
         }
      }

      /* Unknown extension, let's use the whole thing
       * as the symbol and keep going as if nothing
       * happened.
       */
      if( !countryAbbrev )
         ext = NULL; /* No known extension. */
   }

   if( !exchangeString )
   {
      /* If online access is not allowed, and the 
       * symbol does not have a known extension, it
       * is not possible to identify the exchange.
       *
       * With online access, the exchange can be
       * found by doing further investigation on the
       * Yahoo! web sites.
       */
      if( !allowOnlineProcessing )
         return TA_INVALID_SECURITY_EXCHANGE;

      /* OK, we need to proceed by extracting the info
       * from Yahoo! web sites.
       */
      retCode = internalMarketPageAlloc( libHandle,
                                         marketDecodingParam,
                                         yahooSymbol,
                                         &allocatedMarketPage );

      if( retCode != TA_SUCCESS )
         return retCode;

      TA_DEBUG_ASSERT( libHandle, allocatedMarketPage->exchange != NULL );
      TA_DEBUG_ASSERT( libHandle, allocatedMarketPage->type != NULL );

      /* All these string pointer are globals. So the allocatedMarketPage
       * can be freed and the member-poitners are still valid.
       */
      countryAbbrev  = TA_CountryIdToAbbrev( allocatedMarketPage->countryId );
      exchangeString = allocatedMarketPage->exchange;
      typeString     = allocatedMarketPage->type;

      internalMarketPageFree( allocatedMarketPage );
   }   
   
   TA_DEBUG_ASSERT( libHandle, typeString     != NULL );
   TA_DEBUG_ASSERT( libHandle, exchangeString != NULL );
   TA_DEBUG_ASSERT( libHandle, countryAbbrev  != NULL );

   /* Build the Category string into a buffer. */
   tempBuffer = TA_Malloc( libHandle,
                           strlen( countryAbbrev  )  +
                           strlen( exchangeString ) +
                           strlen( typeString )     + 3 );

   sprintf( tempBuffer, "%s.%s.%s", countryAbbrev, exchangeString, typeString );

   /* Allocate the Category string. */
   allocCategory = TA_StringAlloc_UC( stringCache, tempBuffer );

   TA_Free( libHandle, tempBuffer );

   if( !allocCategory )
   {
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   /* Allocate the symbol string. */
   allocSymbol = TA_StringAllocN_UC( stringCache, symbol, symbolLength );

   if( !allocSymbol )
   {
      TA_StringFree( stringCache, allocCategory );
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   /* Everything went fine, return the info to the caller. */
   *allocatedCategoryName = allocCategory;
   *allocatedSymbolName   = allocSymbol;

   TA_TRACE_RETURN( TA_SUCCESS );
}
Example #22
0
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 );
}
Example #23
0
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 );
}
Example #24
0
/**** 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 );
}
Example #25
0
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;
}