static void buildUIRSuffix( const UIRSuffixParsing *parsedString, TA_Timestamp *startTimestamp, TA_Timestamp *endTimestamp, char *output ) { int i; strncpy( output, parsedString->part1, parsedString->part1Length ); i = parsedString->part1Length; sprintf( output, "&a=%02d&b=%02d&c=%04d", TA_GetMonth( startTimestamp )-1, /* Yahoo! month is zero base */ TA_GetDay( startTimestamp ), TA_GetYear( startTimestamp ) ); i += 17; strncpy( &output[i], parsedString->part2, parsedString->part2Length ); i += parsedString->part2Length; sprintf( &output[i], "&d=%02d&e=%02d&f=%04d", TA_GetMonth( endTimestamp )-1, /* Yahoo! month is zero base */ TA_GetDay( endTimestamp ), TA_GetYear( endTimestamp ) ); i += 17; strncpy( &output[i], parsedString->part3, parsedString->part3Length ); i += parsedString->part3Length; output[i] = '\0'; #if DEBUG_PRINTF printf( "New UIR=[%s]\n", output ); #endif }
TA_RetCode TA_ReadOp_Do( TA_FileHandle *fileHandle, const TA_ReadOpInfo *readOpInfo, TA_Period period, const TA_Timestamp *start, const TA_Timestamp *end, unsigned int minimumNbBar, TA_Field fieldToAlloc, TA_ParamForAddData *paramForAddData, unsigned int *nbBarAdded ) { TA_PROLOG TA_RetCode retCode; TA_EstimateInfo estimationInfo; unsigned int nbElementToAllocate; unsigned int memoryNeeded; /* Boolean */ unsigned int timeNeeded; /* Boolean */ TA_Real *arrayReal[TA_REAL_ARRAY_SIZE]; TA_Integer *arrayInteger[TA_INTEGER_ARRAY_SIZE]; TA_Timestamp *timestamp; TA_Real *openBeg, *highBeg, *lowBeg, *closeBeg; TA_Integer *volumeBeg, *openInterestBeg; TA_Timestamp *timestampBeg; TA_Timestamp tmpTimestamp; TA_ReadOp op; TA_Field fieldToProcess; unsigned int year, month, day, hour, min, sec; TA_Integer curOp; unsigned int nbTotalByteDone, nbTotalBarDone; unsigned int nbBarAddedInTheBlock; char monthChar[4]; char cnvtArray[CNVT_ARRAY_SIZE]; unsigned int cnvtArrayIdx; unsigned int nbByteToAllocReal; unsigned int nbByteToAllocInteger; unsigned int fileSize; unsigned int skipField; unsigned int lineToSkip; unsigned int nbByteRead; unsigned int nbLetter; TA_Real lastValidClose; const char *car; register TA_Real tmpReal; register TA_Integer tmpInt; register unsigned int tmpIdx; register unsigned int nbCharToRead; unsigned int lastOpFieldIncremented; TA_TRACE_BEGIN( TA_PriceBarRead ); /* Initialization of local variables. */ openBeg = highBeg = lowBeg = closeBeg = NULL; timestampBeg = NULL; volumeBeg = openInterestBeg = NULL; timestamp = NULL; retCode = TA_SUCCESS; lastValidClose = 0.0; fieldToProcess = readOpInfo->fieldProvided & fieldToAlloc; if( (fieldToProcess & fieldToAlloc) != fieldToAlloc ) { /* Nothing to read because not all the requested * fields are provided by this data source! */ return TA_SUCCESS; } /* Estimate the initial amount of memory to allocate. */ fileSize = TA_FileSize( fileHandle ); retCode = TA_EstimateAllocInit( start, end, period, minimumNbBar, 2048, &estimationInfo, &nbElementToAllocate ); if( retCode != TA_SUCCESS ) { TA_TRACE_RETURN( retCode ); } if( nbElementToAllocate == 0 ) { TA_TRACE_RETURN( TA_SUCCESS ); /* Nothing to read!? Just return... */ } memset( arrayInteger, 0, sizeof( arrayInteger ) ); memset( arrayReal, 0, sizeof( arrayReal ) ); /* Set the date/time pointers to where the information will be stored. */ arrayInteger[TA_HOUR_IDX] = (TA_Integer *)&hour; arrayInteger[TA_MIN_IDX] = (TA_Integer *)&min; arrayInteger[TA_SEC_IDX] = (TA_Integer *)&sec; arrayInteger[TA_MONTH_IDX] = (TA_Integer *)&month; arrayInteger[TA_YEAR_IDX] = (TA_Integer *)&year; arrayInteger[TA_DAY_IDX] = (TA_Integer *)&day; /* Set default time/date. */ year = 1900; month = day = 1; hour = 23; min = sec = 59; /* Check if the processing of the time will be needed. */ timeNeeded = isTimeNeeded( readOpInfo->arrayReadOp); /* 'car' always point to the character being currently handled. */ nbByteRead = 0; car = TA_FileSeqRead( fileHandle, &nbByteRead ); if( (car == NULL) || (nbByteRead == 0) ) return TA_SUCCESS; /* End of file! */ --nbByteRead; nbTotalByteDone = 0; nbTotalBarDone = 0; nbBarAddedInTheBlock = 0; memoryNeeded = 1; curOp = 0; skipField = 0; monthChar[3] = '\0'; /* When requested, skip header lines. */ lineToSkip = readOpInfo->nbHeaderLineToSkip; while( lineToSkip-- ) { while( *car != '\n' ) { GET_CHAR; if( car == NULL ) goto exit_loops; } } line_loop: /* Always jump here when end-of-line is found (EOL). */ /* If curOp != 0, the last operations are canceled. */ REVERT_OPERATIONS; curOp = 0; lastOpFieldIncremented = 0; /* Start over a new line. */ if( memoryNeeded ) { /* Allocate the memory. */ nbByteToAllocReal = nbElementToAllocate * sizeof( TA_Real ); nbByteToAllocInteger = nbElementToAllocate * sizeof( TA_Integer ); timestamp = (TA_Timestamp *)TA_Malloc( nbElementToAllocate * sizeof( TA_Timestamp ) ); timestampBeg = timestamp; if( !timestampBeg ) { retCode = TA_ALLOC_ERR; goto exit_loops; } #define TA_ALLOC_MEM(upperc,lowerc,typepar) \ { \ if( fieldToProcess & TA_##upperc ) \ { \ lowerc##Beg = (TA_##typepar *)TA_Malloc( nbByteToAlloc##typepar ); \ array##typepar[TA_##upperc##_IDX] = lowerc##Beg; \ if( !lowerc##Beg ) \ { \ retCode = TA_ALLOC_ERR; \ goto exit_loops; \ } \ } \ } TA_ALLOC_MEM( OPEN, open, Real ); TA_ALLOC_MEM( HIGH, high, Real ); TA_ALLOC_MEM( LOW, low, Real ); TA_ALLOC_MEM( CLOSE, close, Real ); TA_ALLOC_MEM( VOLUME, volume, Integer ); TA_ALLOC_MEM( OPENINTEREST, openInterest, Integer ); #undef TA_ALLOC_MEM memoryNeeded = 0; } op_loop: /* Jump here when ready to proceed with the next command. */ op = readOpInfo->arrayReadOp[curOp]; if( !(op&TA_CMD_READ_MONTH_CHAR) ) { /* Skip leading non-numeric character. */ SKIP_UNTIL_NUMERIC; } /* Shall we skip this field? */ if( TA_IS_SKIP_SET(op) ) { if( (op&(TA_CMD_READ_REAL|TA_CMD_READ_INTEGER)) == 0 ) { tmpInt = TA_GET_NB_NUMERIC(op); curOp++; while( tmpInt-- ) { GET_CHAR; CHECK_EOL_EOF; } } else { if( skipField == 0 ) { skipField = TA_GET_NB_NUMERIC(op); TA_ASSERT( skipField > 0 ); } if( --skipField == 0 ) curOp++; SKIP_NUMERIC; if( (op&TA_CMD_READ_REAL) && (*car == '.') ) { GET_CHAR; CHECK_EOL_EOF; SKIP_NUMERIC; } } } else { cnvtArrayIdx = 0; if( TA_IS_REAL_CMD(op) ) { /* Extract a numeric into cnvtArray. */ READ_IN_CNVT_ARRAY; /* This is a TA_Real. */ if( car && (*car == '.') ) { /* Read rest of the float after the '.' */ READ_IN_CNVT_ARRAY; } cnvtArray[cnvtArrayIdx] = '\0'; tmpReal = atof( &cnvtArray[0] ); /* Write the TA_Real in memory. */ tmpIdx = TA_GET_IDX(op); TA_ASSERT( tmpIdx < TA_REAL_ARRAY_SIZE ); TA_ASSERT( arrayReal[tmpIdx] != NULL ); if( tmpReal != 0.0 ) { *(arrayReal[tmpIdx]) = tmpReal; if( tmpIdx == TA_CLOSE_IDX ) lastValidClose = tmpReal; } else if( TA_IS_REPLACE_ZERO(op) ) { /* Replace this zero value with the last known close. * If there is no previous close, this line is ignored. */ if( lastValidClose != 0.0 ) *(arrayReal[tmpIdx]) = lastValidClose; else { SKIP_LINE; } } else { /* Zero are not expected, consider this as a failure * and ignore all further data from this file. */ retCode = TA_PRICE_BAR_CONTAINS_ZERO; goto exit_loops; } arrayReal[tmpIdx]++; curOp++; } else { /* This is a TA_Integer. */ if( !(op&TA_CMD_READ_MONTH_CHAR) ) { nbCharToRead = TA_GET_NB_NUMERIC(op); if( nbCharToRead ) { READ_N_CHAR_IN_CNVT_ARRAY(nbCharToRead); } else { READ_IN_CNVT_ARRAY; } cnvtArray[cnvtArrayIdx] = '\0'; tmpInt = atoi( &cnvtArray[0] ); } else { /* Try to find a 3 letters month string. * Translate it to a [1..12] integer. */ nbLetter = 1; do { CHECK_EOL_EOF; monthChar[nbLetter] = (char)toupper(*car); GET_CHAR; nbLetter++; } while( nbLetter != 3 ); do { CHECK_EOL_EOF; monthChar[0] = monthChar[1]; monthChar[1] = monthChar[2]; monthChar[2] = (char)toupper(*car); if( strncmp("JAN",monthChar,3) == 0 ) tmpInt = 1; else if( strncmp("FEB",monthChar,3) == 0 ) tmpInt = 2; else if( strncmp("MAR",monthChar,3) == 0 ) tmpInt = 3; else if( strncmp("APR",monthChar,3) == 0 ) tmpInt = 4; else if( strncmp("MAY",monthChar,3) == 0 ) tmpInt = 5; else if( strncmp("JUN",monthChar,3) == 0 ) tmpInt = 6; else if( strncmp("JUL",monthChar,3) == 0 ) tmpInt = 7; else if( strncmp("AUG",monthChar,3) == 0 ) tmpInt = 8; else if( strncmp("SEP",monthChar,3) == 0 ) tmpInt = 9; else if( strncmp("OCT",monthChar,3) == 0 ) tmpInt = 10; else if( strncmp("NOV",monthChar,3) == 0 ) tmpInt = 11; else if( strncmp("DEC",monthChar,3) == 0 ) tmpInt = 12; else tmpInt = 0; GET_CHAR; } while( tmpInt == 0 ); } /* Write the TA_Integer in memory. */ tmpIdx = TA_GET_IDX(op); TA_ASSERT( tmpIdx < TA_INTEGER_ARRAY_SIZE ); TA_ASSERT( arrayInteger[tmpIdx] != NULL ); *(arrayInteger[tmpIdx]) = tmpInt; if( tmpIdx > TA_YEAR_IDX ) arrayInteger[tmpIdx]++; curOp++; if( TA_IS_TIMESTAMP_COMPLETE(op) ) { /* Build the timestamp. */ retCode = TA_SetDate( year, month, day, &tmpTimestamp ); if( retCode != TA_SUCCESS ) goto exit_loops; /* Invalid date */ if( !timeNeeded ) { /* Ignore time in comparison and use default to build the price bar. */ tmpTimestamp.time = 23595900; /* Default EOD time */ if( start && (tmpTimestamp.date < start->date) ) { /* This price bar is not needed, jump to the next line. */ retCode = TA_SUCCESS; SKIP_LINE; } if( end && (tmpTimestamp.date > end->date) ) { /* This price bar is beyond the upper limit, just exit. */ goto exit_loops; } } else { retCode = TA_SetTime( hour, min, sec, &tmpTimestamp ); if( retCode != TA_SUCCESS ) goto exit_loops; /* Invalid time */ if( start && TA_TimestampLess(&tmpTimestamp,start) ) { /* This price bar is not needed, jump to the next line. */ retCode = TA_SUCCESS; SKIP_LINE; } if( end && TA_TimestampGreater(&tmpTimestamp, end) ) { /* This price bar is beyond the upper limit, just exit. */ goto exit_loops; } } /* Write the timestamp in memory. */ *timestamp = tmpTimestamp; } } if( TA_IS_READ_STOP_FLAG_SET(op) ) { #ifdef DEBUG_PRINTF printf( "(%d%d%d,%e,%e,%e,%e,%d)\n", timestampBeg?TA_GetYear(×tampBeg[nbBarAddedInTheBlock]):0, timestampBeg?TA_GetMonth(×tampBeg[nbBarAddedInTheBlock]):0, timestampBeg?TA_GetDay(×tampBeg[nbBarAddedInTheBlock]):0, openBeg?openBeg[nbBarAddedInTheBlock]:0.0, highBeg?highBeg[nbBarAddedInTheBlock]:0.0, lowBeg?lowBeg[nbBarAddedInTheBlock]:0.0, closeBeg?closeBeg[nbBarAddedInTheBlock]:0.0, volumeBeg?volumeBeg[nbBarAddedInTheBlock]:0 ); #endif /* At this point, the price bar is completely written in memory. */ timestamp++; curOp = 0; nbBarAddedInTheBlock++; if( nbBarAddedInTheBlock < nbElementToAllocate ) { /* Go to next line. */ SKIP_LINE; } else { /* There is not enough memory for another bar, so re-allocate * some more. */ retCode = TA_HistoryAddData( paramForAddData, nbBarAddedInTheBlock, period, timestampBeg, openBeg, highBeg, lowBeg, closeBeg, volumeBeg, openInterestBeg ); /* TA_HistoryAddData is ALWAYS the owner of these memory block * when called. So we set these to NULL to make sure there will * be no attempt to free these from this function. */ openBeg = highBeg = lowBeg = closeBeg = NULL; timestampBeg = NULL; volumeBeg = openInterestBeg = NULL; nbTotalBarDone += nbBarAddedInTheBlock; nbBarAddedInTheBlock = 0; if( retCode != TA_SUCCESS ) goto exit_loops; retCode = TA_EstimateAllocNext( &estimationInfo, &nbElementToAllocate ); if( retCode != TA_SUCCESS ) goto exit_loops; memoryNeeded = 1; SKIP_LINE; } } else { /* Make sure we did not hit an EOL or EOF prematurly, if yes, * this line will be silently ignored. */ CHECK_EOL_EOF; } } goto op_loop; exit_loops: /* Jump here when the end-of-file is hit (or an error occured) */ /* On succesful exit, process possibly remaining data. */ if( (retCode == TA_SUCCESS) && (nbBarAddedInTheBlock != 0) ) { retCode = TA_HistoryAddData( paramForAddData, nbBarAddedInTheBlock, period, timestampBeg, openBeg, highBeg, lowBeg, closeBeg, volumeBeg, openInterestBeg ); openBeg = highBeg = lowBeg = closeBeg = NULL; timestampBeg = NULL; volumeBeg = openInterestBeg = NULL; nbTotalBarDone += nbBarAddedInTheBlock; } /* ALWAYS verify if locally allocated memory needs to be freed. ALWAYS. */ FREE_IF_NOT_NULL( openBeg ); FREE_IF_NOT_NULL( highBeg ); FREE_IF_NOT_NULL( lowBeg ); FREE_IF_NOT_NULL( closeBeg ); FREE_IF_NOT_NULL( volumeBeg ); FREE_IF_NOT_NULL( openInterestBeg ); FREE_IF_NOT_NULL( timestampBeg ); /* An indication that no more data needs to be provided is not a failure. */ if( retCode == TA_ENOUGH_DATA ) retCode = TA_SUCCESS; /* Return the number of added price bar to the caller. */ if( nbBarAdded ) *nbBarAdded = nbTotalBarDone; TA_TRACE_RETURN( retCode ); }
TA_RetCode TA_TradeReportToFile( TA_TradeReport *tradeReport, FILE *out ) { TA_TradeReportPriv *tradeReportPriv; const TA_Timestamp *entryTimestamp; const TA_Timestamp *exitTimestamp; unsigned int i; double tmpReal; if( !tradeReport || !out ) return TA_BAD_PARAM; /* Make sure this TA_TradeReport is a valid object */ tradeReportPriv = (TA_TradeReportPriv *)tradeReport->hiddenData; if( !tradeReportPriv || (tradeReportPriv->magicNb != TA_TRADEREPORT_MAGIC_NB) ) return TA_BAD_OBJECT; fprintf( out, "\n" ); fprintf( out, "[MM/DD/YYYY HH:MM:SS .. MM/DD/YYYY HH:MM:SS] [ID]\n" ); fprintf( out, " Quantity Entry($) Profit(%%) MinFE(%%) MaxFE(%%) MAE(%%)\n" ); fprintf( out, "============================================================\n" ); for( i=0; i < tradeReport->nbTrades; i++ ) { entryTimestamp = &tradeReport->trades[i]->entryTimestamp; exitTimestamp = &tradeReport->trades[i]->exitTimestamp; fprintf( out, "[%02d/%02d/%04d %02d:%02d:%02d .. %02d/%02d/%04d %02d:%02d:%02d] [%s]\n", TA_GetMonth(entryTimestamp), TA_GetDay (entryTimestamp), TA_GetYear (entryTimestamp), TA_GetHour (entryTimestamp), TA_GetMin (entryTimestamp), TA_GetSec (entryTimestamp), TA_GetMonth(exitTimestamp), TA_GetDay (exitTimestamp), TA_GetYear (exitTimestamp), TA_GetHour (exitTimestamp), TA_GetMin (exitTimestamp), TA_GetSec (exitTimestamp), tradeReport->trades[i]->id->symString ); fprintf( out, "%10d", tradeReport->trades[i]->quantity ); fprintf( out, "%10.2f", tradeReport->trades[i]->entryPrice ); fprintf( out, "%10.2f", tradeReport->trades[i]->profit ); tmpReal = tradeReport->trades[i]->minfe; if( tmpReal != TA_REAL_DEFAULT ) fprintf( out, "%10.2f", tmpReal ); else fprintf( out, "%10s", "N/A" ); tmpReal = tradeReport->trades[i]->maxfe; if( tmpReal != TA_REAL_DEFAULT ) fprintf( out, "%10.2f", tmpReal ); else fprintf( out, "%10s", "N/A" ); tmpReal = tradeReport->trades[i]->mae; if( tmpReal != TA_REAL_DEFAULT ) fprintf( out, "%10.2f", tmpReal ); else fprintf( out, "%10s", "N/A" ); fprintf( out, "\n\n" ); } return TA_SUCCESS; }
/**** Global functions definitions. ****/ TA_RetCode TA_GetHistoryDataFromWeb( 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; TA_DecodingParam directYahooDecodingParam; const TA_DecodingParam *decodingParam; TA_FileHandle *fileHandle; TA_ReadOpInfo *readOpInfo; TA_UIRSuffixParsing suffixParsing; TA_Timestamp firstBarTimestamp, lastBarTimestamp, prevEndDate; TA_InfoFromAddedData infoFromAddedData; TA_DayOfWeek dayOfWeek; TA_Timestamp curAdjustYear, lastAdjustYear; const char *overideServerAddr; int nbEstimateBar; int nbField; unsigned int nbBarAdded, nbTotalBarAdded; int again, firstTime, nbBatch; int zeroBarAddedAttempt; int doAdjustment; TA_TRACE_BEGIN( TA_GetHistoryDataFromWeb ); /* Initialize some local variables. */ stringCache = TA_GetGlobalStringCache(); yahooHandle = (TA_PrivateYahooHandle *)handle->opaqueData; readOpInfo = NULL; nbEstimateBar = 0; TA_ASSERT( categoryHandle != NULL ); TA_ASSERT( symbolHandle != NULL ); TA_ASSERT( categoryHandle->string != NULL ); TA_ASSERT( symbolHandle->string != NULL ); retCode = TA_BAD_PARAM; /* Set the initial first/last timestamp */ if( start ) TA_TimestampCopy( &firstBarTimestamp, start ); else TA_SetDate( 1950, 1, 1, &firstBarTimestamp ); if( end ) TA_TimestampCopy( &lastBarTimestamp, end ); else TA_SetDateNow( &lastBarTimestamp ); /* Time component is not important for Yahoo! but all end of day * price bar use 00:00:00, so do the same here. */ TA_SetTime( 0, 0, 0, &firstBarTimestamp ); 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 ); TA_ASSERT( yahooHandle != NULL ); if( yahooHandle->param->id == TA_YAHOO_ONE_SYMBOL ) { /* User specified Yahoo! name. */ yahooName = TA_StringDup(stringCache,yahooHandle->webSiteSymbol); TA_ASSERT( yahooName != NULL ); } else { /* Map the TA-Lib name into the Yahoo! name. */ retCode = TA_AllocStringFromLibName( categoryHandle->string, symbolHandle->string, &yahooName ); if( retCode != TA_SUCCESS ) { TA_TRACE_RETURN( retCode ); } TA_ASSERT( yahooName != NULL ); } /* Check if the user did overide the server address in the location parameter. * (as needed, convert from TA_String to char *) */ if( yahooHandle->userSpecifiedServer ) overideServerAddr = TA_StringToChar(yahooHandle->userSpecifiedServer); else overideServerAddr = NULL; /* Get the decoding parameter for the CSV web page. */ if( yahooHandle->param->id == TA_YAHOO_ONE_SYMBOL ) { retCode = TA_YahooIdxDataDecoding( yahooHandle->webSiteCountry, TA_YAHOOIDX_CSV_PAGE, &directYahooDecodingParam ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); } decodingParam = &directYahooDecodingParam; } else { decodingParam = TA_YahooIdxDecodingParam( yahooHandle->index, TA_YAHOOIDX_CSV_PAGE ); if( !decodingParam ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(103) ); } } localDecodingParam = *decodingParam; /* Check if split/value adjustment are necessary. */ if( (yahooHandle->param->flags & TA_DO_NOT_SPLIT_ADJUST) && (yahooHandle->param->flags & TA_DO_NOT_VALUE_ADJUST) ) { doAdjustment = 0; } else doAdjustment = 1; /* 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) ); } /* 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). */ /* Replace the uirSuffix with a large local buffer. */ localDecodingParam.uirSuffix = TA_Malloc( 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) ) { retCode = TA_DriverShouldContinue(paramForAddData); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif TA_TRACE_RETURN( retCode ); } 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( &localDecodingParam, TA_StringToChar(yahooName), overideServerAddr, &webPage, paramForAddData ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif 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( webPage->content, &fileHandle ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif 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; } #if !defined( TA_SINGLE_THREAD ) /* Must use a local copy if multi-threaded */ retCode = TA_ReadOpInfoClone(&readOpInfo, readOpInfo); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } #endif /* 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( readOpInfo, period, fieldToAlloc ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_WebPageFree( webPage ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif 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( fileHandle, readOpInfo, period, &firstBarTimestamp, &lastBarTimestamp, nbEstimateBar, fieldToAlloc, paramForAddData, &nbBarAdded ); TA_FileSeqClose( fileHandle ); TA_WebPageFree( webPage ); nbTotalBarAdded += nbBarAdded; if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif 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( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif 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( (char *)localDecodingParam.uirSuffix ); TA_StringFree( stringCache, yahooName ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif 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; } /* Get rid of some memory not used anymore. */ TA_Free( (char *)localDecodingParam.uirSuffix ); #if !defined( TA_SINGLE_THREAD ) if( readOpInfo ) TA_ReadOpInfoFree( readOpInfo ); #endif /* If adjusted data is requested, use splits and dividend info from Yahoo!. */ if( doAdjustment && (nbTotalBarAdded >= 1) ) { /* Get the decoding parameter for the adjustment page. */ if( yahooHandle->param->id == TA_YAHOO_ONE_SYMBOL ) { retCode = TA_YahooIdxDataDecoding( yahooHandle->webSiteCountry, TA_YAHOOIDX_ADJUSTMENT, &directYahooDecodingParam ); if( retCode != TA_SUCCESS ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); } decodingParam = &directYahooDecodingParam; } else { decodingParam = TA_YahooIdxDecodingParam( yahooHandle->index, TA_YAHOOIDX_ADJUSTMENT ); if( !decodingParam ) { TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_INTERNAL_ERROR(140) ); } } localDecodingParam = *decodingParam; 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(141) ); } /* 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). */ /* Replace the uirSuffix with a large local buffer. */ localDecodingParam.uirSuffix = TA_Malloc( suffixParsing.maxTotalLength ); if( !localDecodingParam.uirSuffix ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* curAdjustYear indicates for which year the download is * taking place. */ TA_SetDefault( &curAdjustYear ); TA_SetDateNow( &curAdjustYear ); /* Identify the oldest year for which data was downloaded */ TA_GetInfoFromAddedData( paramForAddData, &infoFromAddedData ); TA_TimestampCopy( &lastAdjustYear, &infoFromAddedData.lowestTimestamp ); TA_PrevYear( &lastAdjustYear ); /* Get one more year to be on the safe side. */ while( TA_TimestampLess( &lastAdjustYear, &curAdjustYear ) ) { /* Set prevEndDate to two years earlier. */ TA_TimestampCopy( &prevEndDate, &curAdjustYear ); TA_PrevYear( &prevEndDate ); TA_PrevYear( &prevEndDate ); /* Change the dates in the uirSuffix. */ TA_SetDate( TA_GetYear(&curAdjustYear), 12, 31, &curAdjustYear ); TA_SetDate( TA_GetYear(&prevEndDate), 1, 1, &prevEndDate ); buildUIRSuffix( &suffixParsing, &prevEndDate, &curAdjustYear, (char *)localDecodingParam.uirSuffix ); retCode = doAdjustments( &localDecodingParam, yahooName, yahooHandle, NULL, paramForAddData ); if( retCode != TA_SUCCESS ) { /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_Free( (char *)localDecodingParam.uirSuffix ); TA_TRACE_RETURN( retCode ); } /* Move 3 years earlier. */ TA_PrevYear( &prevEndDate ); TA_TimestampCopy( &curAdjustYear, &prevEndDate ); } /* Clean-up what was allocated for the adjustment logic. */ TA_Free( (char *)localDecodingParam.uirSuffix ); } /* Clean-up and exit */ TA_StringFree( stringCache, yahooName ); TA_TRACE_RETURN( retCode ); }
/**** Local functions definitions. ****/ ErrorNumber test_period( TA_UDBase *unifiedDatabase ) { TA_RetCode retCode; TA_History *history; const TA_Timestamp *timestamp; unsigned int i; ErrorNumber retValue; TA_AddDataSourceParam addParam; TA_HistoryAllocParam histParam; printf( "Testing period/timeframe conversion\n" ); /* Add access to some intra-day data. */ memset( &addParam, 0, sizeof( TA_AddDataSourceParam) ); addParam.id = TA_ASCII_FILE; addParam.location = "..\\src\\tools\\ta_regtest\\sampling\\ES.csv"; addParam.info ="[YY][MM][DD][HH][MN=5][O][H][L][C][V]"; addParam.category = "TA_SIM_REF"; retCode = TA_AddDataSource( unifiedDatabase,&addParam ); if( retCode != TA_SUCCESS ) { printf( "Can't access [%s] (%d)\n", addParam.location, retCode ); return TA_PERIOD_HISTORYALLOC_FAILED; } /* Because period transformation are very * dependend on the "delta" timestamp functions, * let's indepedently verify some of these. */ retValue = testTimestampDelta(); if( retValue != TA_TEST_PASS ) return retValue; /* Verify everything from the checkTable. */ for( i=0; i < CHECK_TABLE_SIZE; i++ ) { /* Validate by requesting all the data in a * different timeframe. */ memset( &histParam, 0, sizeof( TA_HistoryAllocParam ) ); histParam.category = "TA_SIM_REF"; histParam.symbol = checkTable[i].symbol; histParam.field = TA_ALL; histParam.period = checkTable[i].period; histParam.flags = checkTable[i].flags; retCode = TA_HistoryAlloc( unifiedDatabase, &histParam, &history ); if( retCode != TA_SUCCESS ) { printf( "%s%d.1 [%d]\n", fail_header, i, retCode ); return TA_PERIOD_HISTORYALLOC_FAILED; } if( history->nbBars != checkTable[i].nbExpectedPriceBar ) { printf( "%s%d.2 [%d != %d]\n", fail_header, i, history->nbBars, checkTable[i].nbExpectedPriceBar ); TA_HistoryFree( history ); return TA_PERIOD_NBBAR_INCORRECT; } /* If the call is expected to return an empty history, no further check are done. */ if( checkTable[i].nbExpectedPriceBar == 0 ) { retCode = TA_HistoryFree( history ); if( retCode != TA_SUCCESS ) { printf( "%s%d.16 [%d]\n", fail_header, i, retCode ); return TA_PERIOD_HISTORYFREE_FAILED; } continue; } #define CHECK_VALUE_OK(varName, subtestNo ) \ { \ if( !TA_REAL_EQ( history->varName[checkTable[i].thePriceBarToCheck], checkTable[i].expected_##varName, 0.01 ) ) \ { \ printf( "%s%d.%d [%f != %f]\n", \ fail_header, i, subtestNo, \ (TA_Real) history->varName[checkTable[i].thePriceBarToCheck], \ (TA_Real) checkTable[i].expected_##varName ); \ TA_HistoryFree( history ); \ return TA_PERIOD_PRICE_INCORRECT; \ } \ } CHECK_VALUE_OK( open, 3 ); CHECK_VALUE_OK( high, 4 ); CHECK_VALUE_OK( low, 5 ); CHECK_VALUE_OK( close, 6 ); CHECK_VALUE_OK( volume, 7 ); if( history->openInterest != NULL ) { printf( "%s%d.8\n", fail_header, i ); TA_HistoryFree( history ); return TA_PERIOD_OPENINTEREST_INCORRECT; } timestamp = &history->timestamp[checkTable[i].thePriceBarToCheck]; if( TA_GetYear( timestamp ) != checkTable[i].expected_year ) { printf( "%s%d.9 %d\n", fail_header, i, TA_GetYear(timestamp) ); TA_HistoryFree( history ); return TA_PERIOD_TIMESTAMP_YEAR_INCORRECT; } if( TA_GetMonth( timestamp ) != checkTable[i].expected_month ) { printf( "%s%d.10 %d\n", fail_header, i, TA_GetMonth(timestamp) ); TA_HistoryFree( history ); return TA_PERIOD_TIMESTAMP_MONTH_INCORRECT; } if( TA_GetDay( timestamp ) != checkTable[i].expected_day ) { printf( "%s%d.11 %d\n", fail_header, i, TA_GetDay(timestamp) ); TA_HistoryFree( history ); return TA_PERIOD_TIMESTAMP_DAY_INCORRECT; } if( TA_GetHour( timestamp ) != checkTable[i].expected_hour ) { printf( "%s%d.12 %d\n", fail_header, i, TA_GetHour(timestamp) ); TA_HistoryFree( history ); return TA_PERIOD_TIMESTAMP_DAY_INCORRECT; } if( TA_GetMin( timestamp ) != checkTable[i].expected_min ) { printf( "%s%d.13 %d\n", fail_header, i, TA_GetMin(timestamp) ); TA_HistoryFree( history ); return TA_PERIOD_TIMESTAMP_DAY_INCORRECT; } if( TA_GetSec( timestamp ) != checkTable[i].expected_sec ) { printf( "%s%d.14 %d\n", fail_header, i, TA_GetSec(timestamp) ); TA_HistoryFree( history ); return TA_PERIOD_TIMESTAMP_DAY_INCORRECT; } retCode = TA_HistoryFree( history ); if( retCode != TA_SUCCESS ) { printf( "%s%d.15 [%d]\n", fail_header, i, retCode ); return TA_PERIOD_HISTORYFREE_FAILED; } } return 0; /* Succcess. */ }
static TA_RetCode equityPeriodTransform( TA_PMPriv *pmPriv, TA_Period newPeriod, /* The new desired period. */ unsigned int *nbBars, /* Return the number of price bar */ TA_Timestamp **timestamp, /* Allocate new timestamp. */ TA_Real **equity ) /* Allocate new equity. */ { TA_PROLOG /* Notice that this function is very similar to the * TA_PeriodTransform function in ta_period.c * * If you find a bug here, may be worth double * checking TA_PeriodTransform as well... */ TA_RetCode retCode; /* Temporaries. */ const TA_Timestamp *tempTimestamp; unsigned int tempInt, tempInt2; /* Variable used to identify period crossing. */ unsigned int currentWeek, currentMonth, currentYear, currentQuarter; /* Pointer on the history being transformed. */ const TA_Timestamp *old_timestamp; const TA_Real *old_equity; TA_Integer old_nbBars; /* Pointer on the transformed data. */ TA_Timestamp *new_timestamp; /* New allocated timestamp. */ TA_Real *new_equity; /* New allocated open. */ TA_Integer new_nbBars; TA_Timestamp cur_timestamp; /* Current new timestamp of new period. */ TA_Real cur_equity; /* Current new equity of new period. */ int oldPriceBar, newPriceBar; /* Array iterators. */ unsigned int again, periodCompleted; /* Boolean */ int firstIteration; TA_TRACE_BEGIN( TA_PeriodTransform ); /* Validate some mandatory parameter. */ TA_ASSERT( newPeriod != 0 ); TA_ASSERT( nbBars != NULL ); /* It is assume that the caller call this function * when there is really a transform to do. */ TA_ASSERT( newPeriod != TA_DAILY ); /* Of course, timestamps from the source are needed. */ TA_ASSERT( pmPriv->arrayTimestamp != NULL ); /* Initialize all callers pointers to NULL. * These will be initialize only on success. * In the meantime, new_XXXX pointers are * going to be used on the new allocated data. */ if( !timestamp || !equity ) return TA_BAD_PARAM; *timestamp = NULL; *equity = NULL; *nbBars = 0; /* Validate the supported transformation. */ /* Eliminate all the transform that * are currently not supported. * Identify also the major steps * needed to perform the transformation. */ switch( newPeriod ) { case TA_WEEKLY: case TA_MONTHLY: case TA_QUARTERLY: case TA_YEARLY: /* These are supported. */ break; default: TA_TRACE_RETURN( TA_PERIOD_NOT_AVAILABLE ); } /* OK.. now proceed with the transformations. * The strategy is simple: * The equity for the whole period will be the * equity at the last daily price bar of * that period. */ old_timestamp = &pmPriv->arrayTimestamp[0]; old_equity = &pmPriv->equity[0]; old_nbBars = pmPriv->nbDailyBars; new_timestamp = NULL; newPriceBar = 0; cur_timestamp.date = 0; /* Current new timestamp of new period. */ cur_timestamp.time = 0; /* Current new timestamp of new period. */ /* Overestimate the number of required new price bar. */ switch( newPeriod ) { case TA_WEEKLY: retCode = TA_TimestampDeltaWeek( &old_timestamp[0], &old_timestamp[old_nbBars-1], (unsigned int *)&new_nbBars ); break; case TA_MONTHLY: retCode = TA_TimestampDeltaMonth( &old_timestamp[0], &old_timestamp[old_nbBars-1], (unsigned int *)&new_nbBars ); break; case TA_QUARTERLY: retCode = TA_TimestampDeltaQuarter( &old_timestamp[0], &old_timestamp[old_nbBars-1], (unsigned int *)&new_nbBars ); break; case TA_YEARLY: retCode = TA_TimestampDeltaYear( &old_timestamp[0], &old_timestamp[old_nbBars-1], (unsigned int *)&new_nbBars ); break; default: TA_TRACE_RETURN( TA_INTERNAL_ERROR(126) ); } if( retCode != TA_SUCCESS ) { TA_TRACE_RETURN( retCode ); } new_nbBars += 2; /* To be on the safe side */ /* Allocate the new data. */ new_timestamp = TA_Malloc( new_nbBars * sizeof( TA_Timestamp ) ); if( !new_timestamp ) { TA_TRACE_RETURN( TA_ALLOC_ERR ); } new_equity = TA_Malloc( new_nbBars * sizeof( TA_Real ) ); if( !new_equity ) { TA_Free( new_timestamp ); TA_TRACE_RETURN( TA_ALLOC_ERR ); } /* Allows to detect crossing of the new period. */ currentYear = TA_GetYear ( &old_timestamp[0] ); currentMonth = TA_GetMonth( &old_timestamp[0] ); currentWeek = TA_GetWeekOfTheYear( &old_timestamp[0] ); currentQuarter = TA_GetQuarterOfTheYear( &old_timestamp[0] ); /* Iterate through the old price bar. */ oldPriceBar = 0; /* Iterate through the new price bar. */ newPriceBar = 0; again = 1; /* Becomes false when all bars are processed. */ while( again ) { /* Initialize cur_XXXXXX variables with the first bar in old timeframe. */ cur_timestamp = old_timestamp[oldPriceBar]; cur_equity = old_equity [oldPriceBar]; /* Go through the bars and accumulate the info * until the end of the requested period is reach. */ periodCompleted = 0; firstIteration = 1; while( (oldPriceBar < old_nbBars) && !periodCompleted ) { tempTimestamp = &old_timestamp[oldPriceBar]; /* Check if we reached an end of period. */ switch( newPeriod ) { case TA_WEEKLY: tempInt = TA_GetWeekOfTheYear( tempTimestamp ); /* Trap weeks on years boundary. */ if( (currentWeek == 52) && (tempInt == 0) ) currentWeek = 0; else if( currentWeek != tempInt ) { periodCompleted = 1; currentWeek = tempInt; } break; case TA_MONTHLY: tempInt = TA_GetMonth( tempTimestamp ); tempInt2 = TA_GetYear(tempTimestamp); if( (currentMonth != tempInt) || (currentYear != tempInt2) ) { periodCompleted = 1; currentMonth = tempInt; currentYear = tempInt2; } break; case TA_QUARTERLY: tempInt = TA_GetQuarterOfTheYear( tempTimestamp ); tempInt2 = TA_GetYear(tempTimestamp); if( (currentQuarter != tempInt) || (currentYear != tempInt2) ) { periodCompleted = 1; currentQuarter = tempInt; currentYear = tempInt2; } break; case TA_YEARLY: tempInt = TA_GetYear( tempTimestamp ); if( currentYear != tempInt ) { periodCompleted = 1; currentYear = tempInt; } break; default: /* Do nothing */ break; } /* If this is not the end of a period (in the new timeframe) * just accumulate the data. If this is the end of the period * that while loop will be exited. * Nothing is done on the first iteration because all the * cur_XXXX variables have been already initialized. */ if( !periodCompleted ) { if( !firstIteration ) { /* Adjust the new price bar. */ cur_timestamp = old_timestamp[oldPriceBar]; cur_equity = old_equity [oldPriceBar]; } else firstIteration = 0; /* Move to next bar. */ oldPriceBar++; } } /* We got all the info needed in the cur_XXXXX variables for * proceeding with the initialization of the new period price bar. */ TA_DEBUG_ASSERT( newPriceBar < new_nbBars ); /* If the timestamp is requested, some adjustment could be * needed to cur_timestamp. */ switch( newPeriod ) { case TA_WEEKLY: /* Now something a little bit tricky, we must * make sure that this new price bar is reported * as being the Friday of that week (even if there * is no price bar for that Friday). */ TA_JumpToDayOfWeek( &cur_timestamp, TA_FRIDAY ); break; case TA_MONTHLY: /* Monthly timestamp always end with the last day of * the month. Even if there was no actual transaction * the last day. */ TA_JumpToEndOfMonth( &cur_timestamp ); break; case TA_QUARTERLY: /* Quarterly timestamp always end with the last day of * the quarter. Even if there was no actual transaction * the last day. * Quarter 1 = 3/31 * Quarter 2 = 6/30 * Quarter 3 = 9/30 * Quarter 4 = 12/31 */ TA_JumpToEndOfQuarter( &cur_timestamp ); break; case TA_YEARLY: /* Yearly data always end on 12/31. */ TA_JumpToEndOfYear( &cur_timestamp ); break; default: /* Do nothing. */ break; } /* The new price bar is initialized here. */ new_timestamp[newPriceBar] = cur_timestamp; new_equity [newPriceBar] = cur_equity; /* This new period bar is completed, move to the next one. */ newPriceBar++; /* Any more data to process? */ if( oldPriceBar >= old_nbBars) again = 0; /* All bars have been processsed. */ } /* All done! Return the final result to the caller. */ *equity = new_equity; *timestamp = new_timestamp; *nbBars = newPriceBar; TA_TRACE_RETURN( TA_SUCCESS ); }
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; }
TA_RetCode TA_EstimateAllocInit( const TA_Timestamp *start, const TA_Timestamp *end, TA_Period period, unsigned int minimumSize, unsigned int maximumSize, TA_EstimateInfo *estimationInfo, unsigned int *nbElementToAllocate ) { unsigned int nbElement; #if 0 /* Force extrems value for testing. */ *nbElementToAllocate = 1; return TA_SUCCESS; #endif nbElement = minimumSize; /* Default */ if( start && end ) { switch( period ) { case TA_DAILY: TA_TimestampDeltaDay( start, end, &nbElement ); break; case TA_WEEKLY: TA_TimestampDeltaWeek( start, end, &nbElement ); break; case TA_MONTHLY: TA_TimestampDeltaMonth( start, end, &nbElement ); break; case TA_QUARTERLY: TA_TimestampDeltaQuarter( start, end, &nbElement ); break; case TA_YEARLY: TA_TimestampDeltaYear( start, end, &nbElement ); break; default: if( (period >= TA_1SEC) && (period <= TA_1HOUR) ) { /* Estimate the number of day */ if( (TA_GetDay (start) != TA_GetDay (end)) || (TA_GetMonth(start) != TA_GetMonth(end)) || (TA_GetYear (start) != TA_GetYear (end)) ) { /* Estimate assuming market is open for 8 hours per day * (it does not hurt to slightly under or over estimate) */ TA_TimestampDeltaDay( start, end, &nbElement ); nbElement *= (8*60*60); nbElement /= period; } else { nbElement = (8*60*60); nbElement /= period; } } break; } nbElement += 2; } /* Make the estimation fits within the max/min provided. */ estimationInfo->maximumSize = maximumSize; estimationInfo->minimumSize = minimumSize; if( nbElement > maximumSize ) nbElement = maximumSize; if( nbElement < minimumSize ) nbElement = minimumSize; *nbElementToAllocate = nbElement; return TA_SUCCESS; }