/**** 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 ); }
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 }
/* 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 ); }
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; }
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; }
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; }
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 ); }
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; }
TA_RetCode TA_DictAddPair_S2( TA_Dict *dict, TA_String *key1, TA_String *key2, void *value ) { TA_PrivDictInfo *theDict; dnode_t *node; TA_String *dupKey; TA_Dict *subDict; TA_Libc *libHandle; dict_t *kazlibDict; theDict = (TA_PrivDictInfo *)dict; if( (theDict == NULL) || (key1 == NULL) || (key2 == NULL) || (value == NULL) ) return TA_BAD_PARAM; kazlibDict = &theDict->d; libHandle = theDict->libHandle; /* Verify if a a dictionary already exist for key1. */ node = dict_lookup( libHandle, kazlibDict, TA_StringToChar(key1) ); if( node ) { /* A dictionary already exist with the same key1... re-use it. */ subDict = (TA_Dict *)dnode_get( node ); } else { /* Alloc a new directory corresponding to key1. */ subDict = TA_DictAlloc( libHandle, TA_DICT_KEY_ONE_STRING, theDict->freeValueFunc ); if( !subDict ) return TA_ALLOC_ERR; dupKey = TA_StringDup( TA_GetGlobalStringCache( libHandle ), key1 ); if( !dupKey ) { TA_DictFree( subDict ); return TA_ALLOC_ERR; } if( !dict_alloc_insert( libHandle, kazlibDict, TA_StringToChar(dupKey), subDict ) ) { TA_DictFree( subDict ); TA_StringFree( TA_GetGlobalStringCache( libHandle ), dupKey ); return TA_ALLOC_ERR; } } /* Insert the string in the subDict using key2 */ return TA_DictAddPair_S( subDict, key2, value ); }
TA_RetCode TA_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 ); }
/**** 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 ); }
/**** 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 ); }
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 ); }
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; }
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; }
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; }
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; }
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; }
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 ); }
/* 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 ); }
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 ); }
TA_RetCode TA_FileIndexAddCategoryData( TA_FileIndexPriv *data, TA_String *stringCategory, TA_FileIndexCategoryData **added ) { TA_PROLOG; TA_RetCode retCode; TA_FileIndexCategoryData *categoryData; unsigned int tmpInt; unsigned int categoryFound; /* Boolean */ TA_Libc *libHandle; TA_StringCache *stringCache; libHandle = data->libHandle; TA_TRACE_BEGIN( libHandle, TA_FileIndexAddCategoryData ); stringCache = TA_GetGlobalStringCache( libHandle ); TA_ASSERT( libHandle, data != NULL ); TA_ASSERT( libHandle, stringCategory != NULL ); /* Trap the case where the category is already added. */ categoryData = (TA_FileIndexCategoryData *)TA_ListAccessTail( data->listCategory ); categoryFound = 0; while( categoryData && !categoryFound ) { TA_ASSERT( libHandle, categoryData->string != NULL ); tmpInt = strcmp( TA_StringToChar( stringCategory ), TA_StringToChar( categoryData->string ) ); if( tmpInt == 0 ) categoryFound = 1; else categoryData = (TA_FileIndexCategoryData *)TA_ListAccessPrev( data->listCategory ); } if( !categoryFound ) { /* This is a new category, so allocate the TA_FileIndexCategoryData */ categoryData = (TA_FileIndexCategoryData *)TA_Malloc( libHandle, sizeof(TA_FileIndexCategoryData) ); if( !categoryData ) TA_TRACE_RETURN( TA_ALLOC_ERR ); /* Initialize the TA_FileIndexCategoryData */ categoryData->parent = data; if( stringCategory ) { categoryData->string = TA_StringDup( stringCache, stringCategory); /* String for this category. Can be NULL. */ if( !categoryData->string ) { TA_Free( libHandle, categoryData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } } else categoryData->string = NULL; categoryData->listSymbol = TA_ListAlloc( libHandle ); if( !categoryData->listSymbol ) { if( categoryData->string ) TA_StringFree( stringCache, categoryData->string ); TA_Free( libHandle, categoryData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Add it to the TA_FileIndexPriv */ retCode = TA_ListAddTail( data->listCategory, categoryData ); if( retCode != TA_SUCCESS ) { TA_ListFree( categoryData->listSymbol ); if( categoryData->string ) TA_StringFree( stringCache, categoryData->string ); TA_Free( libHandle, categoryData ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } #if 0 printf( "**ADDING CATEGORY[%s]\n", TA_StringToChar( categoryData->string ) ); #endif } /* Return the address of the object representing that category. */ if( added ) *added = categoryData; TA_TRACE_RETURN( TA_SUCCESS ); }
TA_RetCode TA_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 ); }
/**** 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_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; }