static int findTimestampIndex( const TA_PMPriv *pmPriv, const TA_Timestamp *exitTimestamp, int *idx ) { const TA_Timestamp *startDate; const TA_Timestamp *endDate; TA_DayOfWeek dayOfTheWeek; /* Return 0 when no index can be resolved. */ startDate = &pmPriv->startDate; endDate = &pmPriv->endDate; /* Make sure the exitTimestamp is within the start/end date. */ if( (TA_TimestampGreater(exitTimestamp,startDate)&&TA_TimestampLess(exitTimestamp,endDate)) || TA_TimestampEqual(exitTimestamp, startDate) || TA_TimestampEqual(exitTimestamp, endDate) ) { /* Make sure the exitTimestamp is NOT on week-end. Week-end * trades are currently ignored. */ dayOfTheWeek = TA_GetDayOfTheWeek( exitTimestamp ); if( (dayOfTheWeek != TA_SUNDAY) && (dayOfTheWeek != TA_SATURDAY) ) { TA_TimestampDeltaWeekday( startDate, exitTimestamp, (unsigned int *)idx ); *idx -= 1; #ifdef TA_DEBUG TA_ASSERT_RET( *idx >= 0, 0 ); TA_ASSERT_RET( (unsigned int)*idx < pmPriv->nbDailyBars, 0 ); TA_ASSERT_RET( TA_TimestampEqual(&pmPriv->arrayTimestamp[*idx], exitTimestamp ), 0 ); #endif return 1; } } /* No index can be found, initialize to zero, just to * be safe. */ *idx = 0; return 0; }
/* Check if t0 is within the provided [t1..t2] range (inclusive check) */ static unsigned int TA_DateWithinRange( unsigned int year, unsigned int month, unsigned int day, const TA_Timestamp *t1, const TA_Timestamp *t2 ) { TA_Timestamp stamp; const TA_Timestamp *lowBorder; const TA_Timestamp *highBorder; /* Inverse t1 and t2 if not chronilogical order. */ if( TA_TimestampLess( t2, t1 ) ) { lowBorder = t2; highBorder = t1; } else { lowBorder = t1; highBorder = t2; } /* Build a timestamp for the date to be check */ TA_SetDate( year, month, day, &stamp ); /* Check if exactly on boundary */ if( TA_TimestampEqual( &stamp, lowBorder ) && TA_TimestampEqual( &stamp, highBorder ) ) { return 1; } /* Check if within range. */ if( TA_TimestampGreater( &stamp, lowBorder ) && TA_TimestampLess( &stamp, highBorder ) ) { return 1; } return 0; /* Out-of-range */ }
/**** 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 ); }
static ErrorNumber checkRangeSame( TA_UDBase *udb, const TA_History *historyRef, const TA_Timestamp *start, const TA_Timestamp *end, unsigned int startIdx, unsigned int nbPriceBar ) { TA_History *history; unsigned int i; TA_RetCode retCode; #if 1 /* Do not retry... no forgiveness! */ retCode = TA_HistoryAlloc( udb, "CA.CDNX.STOCK", "MRY", TA_DAILY, start, end, TA_ALL, &history ); #else int retry, again; /* Try up to 10 times to get the requested number of * price bar (sometimes Yahoo! or the Web do fail to * return the data). */ again = 1; for( retry=0; (retry < 10) && again; retry++ ) { retCode = TA_HistoryAlloc( udb, "CA.CDNX.STOCK", "MRY", TA_DAILY, start, end, TA_ALL, &history ); if( (retCode == TA_SUCCESS) && (history->nbBars == nbPriceBar) ) again = 0; else { printf( "Warning: Yahoo! history alloc retry #%d of 10\n", retry+1 ); if( retCode == TA_SUCCESS ) { TA_HistoryFree( history ); } TA_Sleep( 10 /* seconds */ ); } } #endif if( retCode != TA_SUCCESS ) { reportError( "TA_HistoryAlloc", retCode ); return TA_YAHOO_CRS_HISTORYALLOC_FAILED; } /* Check that the expected number of price bar is returned. */ if( history->nbBars != nbPriceBar ) { printf( "Failed: nbBars (received != requested)=(%d != %d)\n", history->nbBars, nbPriceBar ); TA_HistoryFree( history ); return TA_YAHOO_CRS_NBBARSBAD; } /* Check that the data is the same for the range. */ for( i=0; i < nbPriceBar; i++ ) { if( !TA_TimestampEqual( &history->timestamp[i], &historyRef->timestamp[startIdx+i] ) || (history->open[i] != historyRef->open[startIdx+i]) || (history->high[i] != historyRef->high[startIdx+i]) || (history->low[i] != historyRef->low[startIdx+i]) || (history->close[i] != historyRef->close[startIdx+i]) || (history->volume[i] != historyRef->volume[startIdx+i]) ) { printf( "Failed: Price Bar value different\n" ); printf( "Failed: Data = %f,%f,%f,%f,%d\n", history->open[i], history->high[i], history->low[i], history->close[i], history->volume[i] ); printf( "Failed: Ref = %f,%f,%f,%f,%d\n", historyRef->open[startIdx+i], historyRef->high[startIdx+i], historyRef->low[startIdx+i], historyRef->close[startIdx+i], historyRef->volume[startIdx+i] ); TA_HistoryFree( history ); return TA_YAHOO_CRS_PRICEBARBAD; } } retCode = TA_HistoryFree( history ); if( retCode != TA_SUCCESS ) { reportError( "TA_HistoryFree", retCode ); return TA_YAHOO_HISTORYFREE_FAILED; } return TA_TEST_PASS; }
static ErrorNumber test_arrayId( TA_PMArrayIdTest *test ) { int i, j; TA_TradeLog *tradeLog; TA_PM *pm; TA_RetCode retCode; TA_Real theValue; TA_PMArray *pmArray; const char *tempStr; /* Allocate and build the TA_TradeLog */ retCode = TA_TradeLogAlloc( &tradeLog ); if( retCode != TA_SUCCESS ) return TA_PM_TEST_ARRAY_ID_FAILED_0; /* Add all the transaction. For simplicity, make these * all the same instrument. */ #define TA_SAFETY_NET_LIMIT 100 i = 0; while( (((void *)test->inputs[i].type) != ((void *)-1)) && (i<TA_SAFETY_NET_LIMIT) ) { retCode = TA_TradeLogAdd( tradeLog, &test->inputs[i] ); if( retCode != TA_SUCCESS ) { printRetCode( retCode ); TA_TradeLogFree( tradeLog ); return TA_PM_TEST_ARRAY_ID_FAILED_1; } i++; } if( i >= TA_SAFETY_NET_LIMIT ) { printf( "Failed: Number of transaction exceed %d limit\n", TA_SAFETY_NET_LIMIT ); return TA_PM_TEST_ARRAY_ID_FAILED_2; } #undef TA_SAFETY_NET_LIMIT /* Build the TA_PM */ retCode = TA_PMAlloc( &test->startDate, &test->endDate, test->initialCapital, &pm ); if( retCode != TA_SUCCESS ) { printRetCode( retCode ); TA_TradeLogFree( tradeLog ); return TA_PM_TEST_ARRAY_ID_FAILED_3; } /* Add the trade log to that PM */ retCode = TA_PMAddTradeLog( pm, tradeLog ); if( retCode != TA_SUCCESS ) { printRetCode( retCode ); return TA_PM_TEST_ARRAY_ID_FAILED_4; } /* Check the requested TA_PMArrayId */ #define TA_SAFETY_NET_LIMIT 30 i=0; while( (((int)test->toCheck[i].id) != -1) && (i<TA_SAFETY_NET_LIMIT) ) { switch( test->toCheck[i].grp ) { case TA_PM_ALL_TRADES: tempStr = "TA_PM_ALL_TRADES"; break; case TA_PM_SHORT_TRADES: tempStr = "TA_PM_SHORT_TRADES"; break; case TA_PM_LONG_TRADES: tempStr = "TA_PM_LONG_TRADES"; break; default: tempStr = "Invalid Group Id"; } retCode = TA_PMArrayAlloc( pm, test->toCheck[i].id, test->toCheck[i].grp, test->toCheck[i].period, &pmArray ); if( (retCode != test->toCheck[i].expectedRetCode) || (pmArray == NULL) ) { printRetCode( test->toCheck[i].expectedRetCode ); printRetCode( retCode ); printf( "Failed: TA_PMArrayAlloc expectedRetCode != retCode (%d != %d)\n", test->toCheck[i].expectedRetCode, retCode ); printf( "Failed: For %d:%s %d:%s Date[%d]\n", test->toCheck[i].id, "", test->toCheck[i].grp, tempStr, (int)test->toCheck[i].date.date ); return TA_PM_TEST_ARRAY_ID_FAILED_5; } if( retCode == TA_SUCCESS ) { /* Find the value to be verified */ for( j=0; j < pmArray->nbData; j++ ) { if( TA_TimestampEqual( &pmArray->timestamp[j], &test->toCheck[i].date ) ) { theValue = pmArray->data[j]; if( !TA_REAL_EQ(theValue,test->toCheck[i].expectedValue,0.01) ) { printf( "Failed: TA_PMArray expectedValue != theValue (%f != %f)\n", test->toCheck[i].expectedValue, theValue ); printf( "Failed: For %d:%s %d:%s, Date[%d]\n", test->toCheck[i].id, "", test->toCheck[i].grp, tempStr, (int)test->toCheck[i].date.date ); return TA_PM_TEST_ARRAY_ID_FAILED_6; } break; } } if( j >= pmArray->nbData ) { printf( "Failed: The expected date [%d] has not been found\n", (int)test->toCheck[i].date.date ); return TA_PM_TEST_ARRAY_WITH_INVALID_DATE; } TA_PMArrayFree( pmArray ); } i++; } if( i >= TA_SAFETY_NET_LIMIT ) { printf( "Failed: Number of checks exceed %d limit\n", TA_SAFETY_NET_LIMIT ); return TA_PM_TEST_ARRAY_ID_FAILED_7; } #undef TA_SAFETY_NET_LIMIT /* Free up everything */ retCode = TA_TradeLogFree( tradeLog ); if( retCode != TA_SUCCESS ) { printRetCode( retCode ); return TA_PM_TEST_ARRAY_ID_FAILED_8; } retCode = TA_PMFree( pm ); if( retCode != TA_SUCCESS ) { printRetCode( retCode ); return TA_PM_TEST_ARRAY_ID_FAILED_9; } return TA_TEST_PASS; }
static TA_Timestamp *allocTimestampArray( const TA_Timestamp *start, const TA_Timestamp *end, int *nbDays ) { TA_RetCode retCode; TA_Timestamp *array; int outIdx; TA_Timestamp curDate; TA_DayOfWeek dayOfTheWeek; TA_ASSERT_RET( TA_TimestampValidate(start) == TA_SUCCESS, (TA_Timestamp *)NULL ); TA_ASSERT_RET( TA_TimestampValidate(end ) == TA_SUCCESS, (TA_Timestamp *)NULL ); TA_ASSERT_RET( nbDays != NULL, (TA_Timestamp *)NULL ); /* Calculate the exact number of week days * between start and end inclusive. * Excluding week-ends. */ retCode = TA_TimestampDeltaWeekday( start, end, (unsigned int *)nbDays ); if( retCode != TA_SUCCESS ) return (TA_Timestamp *)NULL; /* Allocate the array. Add two element just to be on the safe side. */ array = TA_Malloc( sizeof( TA_Timestamp ) * ((*nbDays)+2) ); if( !array ) return (TA_Timestamp *)NULL; /* Fill up the array. */ TA_TimestampCopy( &curDate, start ); /* Write the start point, if it is a weekday. */ outIdx = 0; dayOfTheWeek = TA_GetDayOfTheWeek( &curDate ); if( (dayOfTheWeek != TA_SUNDAY) && (dayOfTheWeek != TA_SATURDAY) ) { TA_TimestampCopy( &array[outIdx], &curDate ); outIdx++; TA_NextWeekday( &curDate ); TA_ASSERT_RET( TA_TimestampValidate(&curDate) == TA_SUCCESS, (TA_Timestamp *)NULL ); } /* Count the number of weekday */ while( TA_TimestampLess( &curDate, end ) ) { TA_TimestampCopy( &array[outIdx], &curDate ); outIdx++; TA_NextWeekday( &curDate ); TA_ASSERT_RET( TA_TimestampValidate(&curDate) == TA_SUCCESS, (TA_Timestamp *)NULL ); } /* Check if the ending point is a weekday. */ if( TA_TimestampEqual( &curDate, end ) ) { dayOfTheWeek = TA_GetDayOfTheWeek( &curDate ); if( (dayOfTheWeek != TA_SUNDAY) && (dayOfTheWeek != TA_SATURDAY) ) TA_TimestampCopy( &array[outIdx++], &curDate ); } TA_ASSERT_RET( outIdx == (*nbDays), (TA_Timestamp *)NULL ); return array; }
static ErrorNumber checkRangeSame( TA_UDBase *udb, const TA_History *historyRef, const TA_Timestamp *start, const TA_Timestamp *end, TA_Period period, unsigned int startIdx, unsigned int nbPriceBar ) { TA_History *history; unsigned int i; TA_RetCode retCode; TA_HistoryAllocParam histParam; int retry, again; /* Try up to 10 times to get the requested number of * price bar (sometimes Yahoo! or the Web do fail to * return the data). */ again = 1; for( retry=0; (retry < 10) && again; retry++ ) { memset( &histParam, 0, sizeof( TA_HistoryAllocParam ) ); histParam.category = "CA.TSE.STOCK"; histParam.symbol = "NT"; histParam.field = TA_ALL; histParam.start = *start; histParam.end = *end; histParam.period = period; retCode = TA_HistoryAlloc( udb, &histParam, &history ); if( (retCode == TA_SUCCESS) && (history->nbBars == nbPriceBar) ) again = 0; else { printf( "Warning: Yahoo! history alloc retry #%d of 10\n", retry+1 ); if( retCode == TA_SUCCESS ) { TA_HistoryFree( history ); } TA_Sleep( 10 /* seconds */ ); } } if( retCode != TA_SUCCESS ) { reportError( "TA_HistoryAlloc", retCode ); return TA_YAHOO_CRS_HISTORYALLOC_FAILED; } /* Check that the expected number of price bar is returned. */ if( history->nbBars != nbPriceBar ) { printf( "Failed: nbBars (received != requested)=(%d != %d)\n", history->nbBars, nbPriceBar ); TA_HistoryFree( history ); return TA_YAHOO_CRS_NBBARSBAD; } /* printf( "startIdx=%d\n", startIdx ); */ /* Check that the data is the same for the range. */ for( i=0; i < nbPriceBar; i++ ) { if( !TA_TimestampEqual( &history->timestamp[i], &historyRef->timestamp[startIdx+i] ) || (history->open[i] != historyRef->open[startIdx+i]) || (history->high[i] != historyRef->high[startIdx+i]) || (history->low[i] != historyRef->low[startIdx+i]) || (history->close[i] != historyRef->close[startIdx+i]) || (history->volume[i] != historyRef->volume[startIdx+i]) ) { printf( "Failed: Price Bar value different\n" ); printf( "Failed: Data = %d/%d/%d : %f,%f,%f,%f,%d\n", TA_GetMonth(&history->timestamp[i]), TA_GetDay(&history->timestamp[i]), TA_GetYear(&history->timestamp[i]), history->open[i], history->high[i], history->low[i], history->close[i], history->volume[i] ); printf( "Failed: Ref = %d/%d/%d : %f,%f,%f,%f,%d\n", TA_GetMonth(&historyRef->timestamp[i]), TA_GetDay(&historyRef->timestamp[i]), TA_GetYear(&historyRef->timestamp[i]), historyRef->open[startIdx+i], historyRef->high[startIdx+i], historyRef->low[startIdx+i], historyRef->close[startIdx+i], historyRef->volume[startIdx+i] ); TA_HistoryFree( history ); return TA_YAHOO_CRS_PRICEBARBAD; } } retCode = TA_HistoryFree( history ); if( retCode != TA_SUCCESS ) { reportError( "TA_HistoryFree", retCode ); return TA_YAHOO_HISTORYFREE_FAILED; } return TA_TEST_PASS; }