Пример #1
0
TA_RetCode TA_PMAlloc( const TA_Timestamp  *startDate,
                       const TA_Timestamp  *endDate,
                       TA_Real              initialCapital,
                       TA_PM              **allocatedPM )
{
    TA_PM     *pm;
    TA_PMPriv *pmPriv;
    unsigned int delta;
    TA_RetCode retCode;

    /* Check all the parameters. */
    if( !allocatedPM )
        return TA_BAD_PARAM;
    *allocatedPM = NULL;

    if( !startDate || !endDate )
        return TA_BAD_PARAM;

    if( TA_TimestampValidate( startDate ) )
        return TA_BAD_START_DATE;

    if( TA_TimestampValidate( endDate ) || TA_TimestampGreater( startDate, endDate ) )
        return TA_BAD_END_DATE;

    /* To keep things simple, it is assumed that
     * the requested date range contains at least
     * one weekday.
     */
    retCode = TA_TimestampDeltaWeekday( startDate, endDate, &delta );
    if( retCode != TA_SUCCESS )
        return retCode;
    if( delta <= 0 )
        return TA_NO_WEEKDAY_IN_DATE_RANGE;

    /* Allocate the public and private structure. */
    pm = TA_Malloc( sizeof( TA_PM ) + sizeof( TA_PMPriv ) );
    if( !pm )
        return TA_ALLOC_ERR;

    memset( pm, 0, sizeof( TA_PM ) + sizeof( TA_PMPriv ) );
    pmPriv = (TA_PMPriv *)(((char *)pm)+sizeof(TA_PM));
    pmPriv->magicNb        = TA_PMPRIV_MAGIC_NB;
    pmPriv->initialCapital = initialCapital;
    pm->hiddenData         = pmPriv;

    TA_ListInit(  &pmPriv->tradeLogList );

    /* TA_PMFree can be safely called from this point. */

    TA_TimestampCopy( &pmPriv->endDate, endDate );
    TA_TimestampCopy( &pmPriv->startDate, startDate );

    /* Success, return the allocated data to the caller. */
    *allocatedPM = pm;

    return TA_SUCCESS;
}
Пример #2
0
static int parseDate( const char *str, TA_Timestamp *timestamp )
{
   TA_Timestamp temp;
   int day, month, year;
   TA_RetCode retCode;
   int found;
   static const char *monthStr[12] = { "Jan", "Feb", "Mar",
                                       "Apr", "May", "Jun",
                                       "Jul", "Aug", "Sep",
                                       "Oct", "Nov", "Dec" };
   if( !timestamp || !str )
      return 0;

   /* Parse day */
   if( sscanf( str, "%d", &day ) != 1 )
      return 0;

   /* Parse Month */
   found = 0;
   month = 0;
   while( !found && (month < 12) )
   {
      if( strstr(str, monthStr[month] ) )
         found = 1;
      else
         month++;     
   }
   if( !found ) return 0;

   /* Parse Year */
   str = strchr( str, '-' );
   if( !str || str[1] == '\0' )
      return 0;
   str = strchr( str+1, '-' );
   if( !str || str[1] == '\0' )
      return 0;
   if( sscanf( str+1, "%d", &year ) != 1 )
      return 0;

   /* Build timestamp */
   TA_SetDefault( &temp );
   retCode = TA_SetDate( year, month+1, day, &temp );
   if( retCode != TA_SUCCESS )
      return 0;

   retCode = TA_TimestampCopy( timestamp, &temp );
   if( retCode != TA_SUCCESS )
      return 0;
   
   return 1;
}
Пример #3
0
/**** Global functions definitions.   ****/
TA_RetCode TA_GetHistoryDataFromWeb( TA_Libc *libHandle,
                                     TA_DataSourceHandle *handle,
                                     TA_CategoryHandle   *categoryHandle,
                                     TA_SymbolHandle     *symbolHandle,
                                     TA_Period            period,
                                     const TA_Timestamp  *start,
                                     const TA_Timestamp  *end,
                                     TA_Field             fieldToAlloc,
                                     TA_ParamForAddData  *paramForAddData )
{
   TA_PROLOG;

   TA_RetCode retCode;
   TA_StringCache *stringCache;
   TA_String *yahooName;
   TA_WebPage *webPage;
   TA_PrivateYahooHandle *yahooHandle;
   TA_DecodingParam localDecodingParam;
   const TA_DecodingParam *decodingParam;
   TA_FileHandle *fileHandle;
   TA_ReadOpInfo *readOpInfo;
   UIRSuffixParsing suffixParsing;
   TA_Timestamp firstBarTimestamp, lastBarTimestamp, prevEndDate;
   TA_InfoFromAddedData infoFromAddedData;
   TA_DayOfWeek dayOfWeek;

   int nbEstimateBar;
   int nbField;
   unsigned int nbBarAdded, nbTotalBarAdded;
   int again, firstTime, nbBatch;
   int zeroBarAddedAttempt;

   TA_TRACE_BEGIN( libHandle, TA_GetHistoryDataFromWeb );

   /* Initialize some local variables. */
   stringCache   = TA_GetGlobalStringCache( libHandle );
   yahooHandle   = (TA_PrivateYahooHandle *)handle->opaqueData;
   readOpInfo    = NULL;
   nbEstimateBar = 0;

   TA_ASSERT( libHandle, categoryHandle != NULL );
   TA_ASSERT( libHandle, symbolHandle != NULL );
   TA_ASSERT( libHandle, categoryHandle->string != NULL );
   TA_ASSERT( libHandle, symbolHandle->string != NULL );

   /* Set the initial first/last timestamp */
   if( start )
      TA_TimestampCopy( &firstBarTimestamp, start );
   else
   {
      TA_SetDate( 1950, 1, 1, &firstBarTimestamp );
      TA_SetTime( 0, 0, 0, &firstBarTimestamp );
   }

   if( end )
      TA_TimestampCopy( &lastBarTimestamp, end );
   else
   {
      TA_SetDateNow( &lastBarTimestamp );
      TA_SetTime( 0, 0, 0, &lastBarTimestamp );
   }

   /* Make sure that lastBarTimestamp is a week-day. */
   dayOfWeek = TA_GetDayOfTheWeek( &lastBarTimestamp );
   if( (dayOfWeek == TA_SUNDAY) || (dayOfWeek == TA_SATURDAY) )
      TA_PrevWeekday( &lastBarTimestamp );

   /* Map the TA-Lib name into the Yahoo! name. */
   retCode = TA_AllocStringFromLibName( libHandle,
                                        categoryHandle->string,
                                        symbolHandle->string,
                                        &yahooName );  
   if( retCode != TA_SUCCESS )
   {
      TA_TRACE_RETURN( retCode );
   }

   TA_ASSERT( libHandle, yahooName != NULL );
   TA_ASSERT( libHandle, yahooHandle != NULL );

   /* Get the decoding parameter for the CSV web page. */
   decodingParam = TA_YahooIdxDecodingParam( yahooHandle->index, TA_YAHOOIDX_CVS_PAGE );
   if( !decodingParam )
   {
      TA_StringFree( stringCache, yahooName );
      TA_TRACE_RETURN( TA_INTERNAL_ERROR(103) );
   }

   /* Use a local copy of the decoding param. 
    * This is because the uirSuffix is replaced with
    * an allocated buffer (so the date field can be
    * manipulated).
    */
   localDecodingParam = *decodingParam;

   /* Parse the uirSuffix so the start/end date can be changed. */
   if( !setUIRSuffixParsing( decodingParam->uirSuffix, &suffixParsing ) )
   {
      /* This should never happen unless the
       * Yahoo! index protocol has been broken.
       */
      /* Clean-up and exit */
      TA_StringFree( stringCache, yahooName );
      TA_TRACE_RETURN( TA_INTERNAL_ERROR(104) );
   }

   /* Replace the uirSuffix with a large local buffer. */
   localDecodingParam.uirSuffix = TA_Malloc( libHandle, suffixParsing.maxTotalLength );
   if( !localDecodingParam.uirSuffix )
   {
      /* Clean-up and exit */
      TA_StringFree( stringCache, yahooName );
      TA_TRACE_RETURN( TA_ALLOC_ERR );
   }

   /* Change the dates in the uirSuffix. */
   buildUIRSuffix( &suffixParsing,
                   &firstBarTimestamp, &lastBarTimestamp,
                   (char *)localDecodingParam.uirSuffix );

   /* nbBatch is a safety net to make sure that
    * TA-Lib won't stay forever in the while loop
    * in case Yahoo! changes their protocol.
    */
   nbBatch = 0; 

   /* Sometime Yahoo! return an empty csv file. Make
    * multiple attempts in that case.
    */
   zeroBarAddedAttempt = 0;

   again = 1;
   firstTime = 1;
   nbTotalBarAdded = 0;
   while( again && (++nbBatch < 100) && (zeroBarAddedAttempt < 10) )
   {  
    
      if( TA_TimestampLess(&lastBarTimestamp,&firstBarTimestamp) )
      {
          /* Get out of this loop if all the requested data
           * has been retreived already.
           */
         again = 0;
         break;
      }   

      retCode = TA_WebPageAllocFromYahooName( libHandle,
                                              &localDecodingParam,
                                              TA_StringToChar(yahooName),
                                              &webPage );
           
      if( retCode != TA_SUCCESS )
      {
         TA_StringFree( stringCache, yahooName );
         TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
         TA_TRACE_RETURN( retCode );
      }

      /* Disguise the webPage stream into a "file". That way the speed
       * optimized ASCII decoder can be re-used (TA_ReadOp stuff).
       */
      retCode = TA_FileSeqOpenFromStream( libHandle, webPage->content, &fileHandle );
      if( retCode != TA_SUCCESS )
      {
         /* Clean-up and exit */
         TA_StringFree( stringCache, yahooName );
         TA_WebPageFree( webPage );
         TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
         TA_TRACE_RETURN( retCode );
      }

      if( firstTime )
      {
         /* Make assumption of the data provided
          * base on the number of fields in the CSV file.
          */
         nbField = nbCommaField( webPage->content );
         switch( nbField )
         {
         case 2:
            readOpInfo = yahooHandle->readOp2Fields;
            break;
         case 5:
            readOpInfo = yahooHandle->readOp5Fields;
            break;
         default:
            readOpInfo = yahooHandle->readOp6Fields;
         }

         /* User asking for all the fields? */
         if( fieldToAlloc == TA_ALL )
         {
            switch( nbField )
            {
            case 2:
               fieldToAlloc = TA_CLOSE|TA_TIMESTAMP;
               break;
            case 5:
               fieldToAlloc = TA_OPEN|TA_HIGH|TA_LOW|TA_CLOSE|TA_TIMESTAMP;
               break;
            default:
               fieldToAlloc = TA_OPEN|TA_HIGH|TA_LOW|TA_CLOSE|TA_VOLUME|TA_TIMESTAMP;
            }
         }

         /* Optimize the read op for the requested data. */
         retCode = TA_ReadOp_Optimize( libHandle,
                                       readOpInfo,
                                       period,
                                       fieldToAlloc );
         if( retCode != TA_SUCCESS )
         {
            /* Clean-up and exit */
            TA_StringFree( stringCache, yahooName );
            TA_WebPageFree( webPage );
            TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
            TA_TRACE_RETURN( retCode );
         }

         /* Make an estimation of the number of price bar. */
         nbEstimateBar  = TA_StreamCountChar( webPage->content, '\n' ) + 1;
         if( nbEstimateBar < 100 )
            nbEstimateBar = 100;
      }

      /* Interpret the CSV data. */
      retCode = TA_ReadOp_Do( libHandle, fileHandle,                           
                              readOpInfo,
                              period, &firstBarTimestamp, &lastBarTimestamp,
                              nbEstimateBar, fieldToAlloc,
                              paramForAddData,
                              &nbBarAdded );

      TA_FileSeqClose( libHandle, fileHandle );
      TA_WebPageFree( webPage );

      nbTotalBarAdded += nbBarAdded;

      if( retCode != TA_SUCCESS )
      {
         /* Clean-up and exit */
         TA_StringFree( stringCache, yahooName );
         TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
         TA_TRACE_RETURN( retCode );
      }

      /* Yahoo! does not always return all the data it could, up to
       * the requested end date. It is important to detect these occurence
       * and cancel the usage of all data accumulated up to now. 
       */      
      TA_GetInfoFromAddedData( paramForAddData, &infoFromAddedData );
      if( infoFromAddedData.barAddedSinceLastCall )
      {
         /* Do some more checking by considering holidays, week-end etc... */
         if( !isGapAcceptable(&infoFromAddedData.highestTimestampAddedSinceLastCall, &lastBarTimestamp) )
         {
            /* Clean-up and exit */
            TA_StringFree( stringCache, yahooName );
            TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
            TA_TRACE_RETURN( TA_DATA_GAP );
         }
         
         TA_TimestampCopy( &lastBarTimestamp, &infoFromAddedData.lowestTimestamp );
      }

      #if DEBUG_PRINTF
      printf( "NB BAR ADDED=%d, TOTAL=%d\n", nbBarAdded, nbTotalBarAdded );
      #endif

      /* Verify if more data should be processed. 
       * Yahoo! sometimes slice their data, in 
       * batch of 200 price bars. 
       */
      if( firstTime && (nbBarAdded > 200) )
      {
         again = 0; /* Assume all the data extracted... exit the loop. */
      }
      else if( nbBarAdded == 0 )
      {
         /* Make multiple attempts when retreiving data succeed,
          * but somehow there is zero bar returned. 
          *
          * Sometimes this might be correct when there is truly no
          * more data available, so choosing an algorithm before
          * giving up is a comprimise between reliability and
          * usability. The data source is free... and you get
          * what you pay for after all ;)
          */
         if( (nbTotalBarAdded < 1000) && (zeroBarAddedAttempt >= 1) && (zeroBarAddedAttempt < 7) )
         {
            /* I did choose to add a delay when insufficient total data is returned. When
             * there is already ~5 years of data, most likely there is "Zero" returned
             * because there is NO more data available, so just do the retry without delay.
             */
            TA_Sleep(zeroBarAddedAttempt*2);
         }

         #if DEBUG_PRINTF
         printf( "Retry %d", zeroBarAddedAttempt );
         #endif

         zeroBarAddedAttempt++;
      }
      else
      {
         zeroBarAddedAttempt = 0;

         if( TA_TimestampEqual( &lastBarTimestamp, &prevEndDate ) )
         {
            /* prevEndDate is a "safety net" to
             * exit the loop early in case Yahoo! starts
             * to return always the same batch of data.
             * Just ignore the repetitive data and exit.
             */
            TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
            TA_StringFree( stringCache, yahooName );
            TA_TRACE_RETURN( TA_SUCCESS );
         }
         TA_TimestampCopy( &prevEndDate, &lastBarTimestamp );

         /* Request the data up to the day BEFORE
          * the last batch of data received.
          */
         TA_PrevDay( &lastBarTimestamp );

         /* Make sure that lastBarTimestamp is a week-day. */
         dayOfWeek = TA_GetDayOfTheWeek( &lastBarTimestamp );
         if( (dayOfWeek == TA_SUNDAY) || (dayOfWeek == TA_SATURDAY) )
            TA_PrevWeekday( &lastBarTimestamp );

         /* Change the dates in the uirSuffix. */
         buildUIRSuffix( &suffixParsing,
                         &firstBarTimestamp, &lastBarTimestamp,
                         (char *)localDecodingParam.uirSuffix );

         /* From that point, data is expected to be most likely
          * sent in batch of 200.
          */
         nbEstimateBar = 200;
      }

      firstTime = 0;
   }

   /* Clean-up and exit */
   TA_Free( libHandle, (char *)localDecodingParam.uirSuffix );
   TA_StringFree( stringCache, yahooName );
   TA_TRACE_RETURN( retCode );
}
Пример #4
0
TA_RetCode TA_SIMULATOR_GetHistoryData( 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_PrivateHandle *privateHandle;
   TA_RetCode retCode;
   TA_Timestamp *timestamp;
   TA_Real *open, *high, *low, *close;
   TA_Integer *volume;
   unsigned int i;

   (void)fieldToAlloc;
   (void)end;
   (void)start;
   (void)period;

   TA_TRACE_BEGIN(  TA_SIMULATOR_GetHistoryData );

   TA_ASSERT( handle != NULL );

   privateHandle = (TA_PrivateHandle *)handle->opaqueData;
   TA_ASSERT( privateHandle != NULL );
   TA_ASSERT( paramForAddData != NULL );
   TA_ASSERT( categoryHandle != NULL );
   TA_ASSERT( symbolHandle != NULL );

   retCode = TA_INTERNAL_ERROR(98);

   /* Note: start/end index are currently ignored
    *       in this data source.
    */

   /* Identify the category. */
   switch( (unsigned int)categoryHandle->opaqueData )
   {
   case 0: /* This is TA_SIM_REF */
      switch( (unsigned int)symbolHandle->opaqueData )
      {
      case 0:
          timestamp = (TA_Timestamp *)NULL;
          open = high = low = close = (TA_Real *)NULL;
          volume = (TA_Integer *)NULL;

          #define TA_ALLOC_COPY( varName, varType, varSize) { \
             varName = TA_Malloc( sizeof( varType ) * varSize ); \
             if( !varName ) \
             { \
                FREE_IF_NOT_NULL( open         ); \
                FREE_IF_NOT_NULL( high         ); \
                FREE_IF_NOT_NULL( low          ); \
                FREE_IF_NOT_NULL( close        ); \
                FREE_IF_NOT_NULL( volume       ); \
                TA_TRACE_RETURN( TA_ALLOC_ERR ); \
             } \
             memcpy( varName, TA_SREF_##varName##_daily_ref_0_PRIV, sizeof( varType )*varSize ); }

             TA_ALLOC_COPY( open,      TA_Real,      TA_REF_DAILY_NB_BARS );
             TA_ALLOC_COPY( high,      TA_Real,      TA_REF_DAILY_NB_BARS );
             TA_ALLOC_COPY( low,       TA_Real,      TA_REF_DAILY_NB_BARS );
             TA_ALLOC_COPY( close,     TA_Real,      TA_REF_DAILY_NB_BARS );
             TA_ALLOC_COPY( volume,    TA_Integer,   TA_REF_DAILY_NB_BARS );         
         #undef TA_ALLOC_COPY

         /* Set the timestamp. */
         timestamp = TA_Malloc( sizeof( TA_Timestamp ) * TA_REF_DAILY_NB_BARS );
         if( !timestamp )
         {
            TA_Free(  open   );
            TA_Free(  high   );
            TA_Free(  low    );
            TA_Free(  close  );
            TA_Free(  volume );
            TA_TRACE_RETURN( TA_ALLOC_ERR );
         }

         for( i=0; i < TA_REF_DAILY_NB_BARS; i++ )
            TA_TimestampCopy( &timestamp[i], &TA_SREF_timestamp_daily_ref_0_PRIV[i] );

          retCode = TA_HistoryAddData( paramForAddData,
                                       TA_REF_DAILY_NB_BARS, TA_DAILY,                                      
                                       timestamp,
                                       open, high,                                      
                                       low, close,
                                       volume, NULL );                                      
          break;

      case 1:
          /* Allocate the rest. */
          timestamp = (TA_Timestamp *)NULL;
          open = high = low = close = (TA_Real *)NULL;

          #define TA_ALLOC_COPY( varName, varType, varSize) { \
                  varName = TA_Malloc( sizeof( varType ) * varSize ); \
                  if( !varName ) \
                  { \
                     TA_Free(  timestamp ); \
                     FREE_IF_NOT_NULL( open      ); \
                     FREE_IF_NOT_NULL( high      ); \
                     FREE_IF_NOT_NULL( low       ); \
                     FREE_IF_NOT_NULL( close     ); \
                     TA_TRACE_RETURN( TA_ALLOC_ERR ); \
                  } \
                  memcpy( varName, TA_SREF_##varName##_daily_ref_0_PRIV, sizeof( varType )*varSize ); }

         TA_ALLOC_COPY( open,      TA_Real,      33 );
         TA_ALLOC_COPY( high,      TA_Real,      33 );
         TA_ALLOC_COPY( low,       TA_Real,      33 );
         TA_ALLOC_COPY( close,     TA_Real,      33 );
         #undef TA_ALLOC_COPY

         /* Set the timestamp. */
         timestamp = (TA_Timestamp *)TA_Malloc( sizeof( TA_Timestamp ) * 33 );
         if( !timestamp )
         {
            FREE_IF_NOT_NULL( open  );
            FREE_IF_NOT_NULL( high  );
            FREE_IF_NOT_NULL( low   );
            FREE_IF_NOT_NULL( close );
            TA_TRACE_RETURN( TA_ALLOC_ERR );
         }

         for( i=0; i < TA_REF_INTRA_NB_BARS; i++ )
            TA_TimestampCopy( &timestamp[i], &TA_SREF_timestamp_intra_ref_0_PRIV[i] );

         retCode = TA_HistoryAddData( paramForAddData,
                                      TA_REF_INTRA_NB_BARS, TA_1MIN*10,
                                      timestamp,
                                      open, high,                                      
                                      low, close,                                      
                                      NULL, NULL );
                                      
         break;
      }
      break;

   case 1: /* This is TA_SIM_MRG */
      retCode = addSimMrgData( privateHandle, 
                               paramForAddData );
      break;
   }

   TA_TRACE_RETURN( retCode );
}
Пример #5
0
TA_RetCode TA_TradeLogAdd( TA_TradeLog    *tradeLog,
                           const TA_Transaction *newTransaction )
{
    TA_TradeLogPriv *tradeLogPriv;
    TA_Instrument *id;
    TA_Dict *theDict;
    TA_DataLog *dataLog;
    TA_TradeDictEntry *dictEntry;
    TA_StringCache *stringCache;
    TA_String *catString;
    TA_String *symString;
    const char *catCharPtr;
    const char *symCharPtr;
    TA_RetCode retCode;
    int quantity, entryTradeQuantity;
    TA_List *entryListToUse;
    TA_DataLog *entryTradeLog;

    TA_Real highestLow, highestHigh, lowestLow, lowestHigh;
    TA_Real entryPrice, tempReal;
    int i;

    retCode = TA_INTERNAL_ERROR(120);

    /* This function will transform the TA_Transaction into
     * an "entry" or multiple "trades" (because an exit can
     * be translated into multiple trade if there was multiple
     * entry point).
     */
    if( !tradeLog || !newTransaction )
        return TA_BAD_PARAM;

    /* Check that the TA_Transaction makes sense. */
    if( (newTransaction->price <= 0.0)  ||
            (newTransaction->quantity <= 0) ||
            (TA_TimestampValidate(&newTransaction->timestamp) != TA_SUCCESS) ||
            (newTransaction->type >= TA_NB_TRADE_TYPE))
        return TA_BAD_PARAM;

    /* Get access to the hidden data of the TA_TradeLog. */
    tradeLogPriv = (TA_TradeLogPriv *)tradeLog->hiddenData;

    /* Make sure this is a valid object. */
    if( !tradeLogPriv || (tradeLogPriv->magicNb != TA_TRADELOGPRIV_MAGIC_NB) )
        return TA_BAD_OBJECT;

    /* Find the TA_TradeDictEntry corresponding to
     * the TA_Instrument.
     *
     * Use the dictionary corresponding to the type of
     * key of the TA_Instrument.
     *
     * If TA_Instrument is NULL, use the pre-allocated
     * default TA_TradeDictEntry.
     */
    id = newTransaction->id;
    if( !id )
    {
        dictEntry = &tradeLogPriv->defaultDictEntry;
        catCharPtr = NULL;
        symCharPtr = NULL;
        theDict    = NULL;
    }
    else
    {
        catCharPtr = id->catString;
        symCharPtr = id->symString;
        if( catCharPtr )
        {
            if( symCharPtr )
            {
                theDict = tradeLogPriv->tradeDictCATSYM;
                dictEntry = TA_DictGetValue_S2( theDict, catCharPtr, symCharPtr );
            }
            else
            {
                theDict = tradeLogPriv->tradeDictCAT;
                dictEntry = TA_DictGetValue_S( theDict, catCharPtr );
            }
        }
        else if( symCharPtr )
        {
            theDict = tradeLogPriv->tradeDictCAT;
            dictEntry = TA_DictGetValue_S( theDict, symCharPtr );
        }
        else
        {
            theDict = tradeLogPriv->tradeDictUserKey;
            dictEntry = TA_DictGetValue_I( theDict, id->userKey );
        }
    }

    if( !dictEntry )
    {
        if( !theDict )
            return TA_INTERNAL_ERROR(146);

        /* The TA_TradeDictEntry was not found, create it! */
        dictEntry = TA_Malloc( sizeof( TA_TradeDictEntry ) );
        if( !dictEntry )
            return TA_ALLOC_ERR;

        memset( &dictEntry->id, 0, sizeof(TA_Instrument) );
        TA_ListInit( &dictEntry->shortEntryPrivList );
        TA_ListInit( &dictEntry->longEntryPrivList );

        /* Add the dictEntry to the corresponding dictionary. */
        stringCache = TA_GetGlobalStringCache();

        if( catCharPtr )
        {
            catString = TA_StringAlloc( stringCache, catCharPtr );
            if( !catString )
            {
                TA_Free( dictEntry );
                return TA_ALLOC_ERR;
            }

            if( symCharPtr )
            {
                symString = TA_StringAlloc( stringCache, symCharPtr );
                if( !symString )
                {
                    TA_Free( dictEntry );
                    TA_StringFree( stringCache, catString );
                    return TA_ALLOC_ERR;
                }
                retCode = TA_DictAddPair_S2( theDict, catString, symString, dictEntry );
                dictEntry->id.symString = TA_StringToChar(symString);
            }
            else
                retCode = TA_DictAddPair_S( theDict, catString, dictEntry );

            dictEntry->id.catString = TA_StringToChar(catString);
        }
        else if( symCharPtr )
        {
            symString = TA_StringAlloc( stringCache, symCharPtr );
            if( !symString )
            {
                TA_Free( dictEntry );
                return TA_ALLOC_ERR;
            }

            retCode = TA_DictAddPair_S( theDict, symString, dictEntry );
            dictEntry->id.symString = TA_StringToChar(symString);
        }
        else
        {
            retCode = TA_DictAddPair_I( theDict, id->userKey, dictEntry );
            dictEntry->id.userKey = id->userKey;
        }

        /* Check the retCode of the TA_DictAddXXXXX function. */
        if( retCode != TA_SUCCESS )
        {
            TA_Free( dictEntry );
            return TA_ALLOC_ERR;
        }
    }

    /* Identify the approriate list of entry. */
    switch( newTransaction->type )
    {
    case TA_LONG_ENTRY:
    case TA_LONG_EXIT:
        entryListToUse = &dictEntry->longEntryPrivList;
        break;
    case TA_SHORT_ENTRY:
    case TA_SHORT_EXIT:
        entryListToUse = &dictEntry->shortEntryPrivList;
        break;
    default:
        return TA_BAD_PARAM;
    }

    /* The sign of the quantity indicates if the
     * data log is a completed trade (+) or an
     * entry (-).
     */
    switch( newTransaction->type )
    {
    case TA_LONG_ENTRY:
    case TA_SHORT_ENTRY:
        /* Allocate a data log and add it to the list. */
        dataLog = TA_AllocatorForDataLog_Alloc( &tradeLogPriv->allocator );
        if( !dataLog )
            return TA_ALLOC_ERR;
        dataLog->u.entry.quantity  = -(newTransaction->quantity);
        dataLog->u.entry.entryPrice = newTransaction->price;
        TA_TimestampCopy( &dataLog->u.entry.entryTimestamp,
                          &newTransaction->timestamp );
        TA_ListNodeAddTail( entryListToUse, &dataLog->u.entry.node, dataLog );
        break;

    case TA_LONG_EXIT:
    case TA_SHORT_EXIT:
        /* Invalidate cached calculation of this trade log. */
        tradeLogPriv->flags &= ~TA_PMVALUECACHE_CALCULATED;

        /* Transform this transaction into one or
         * multiple trade(s).
         */
        entryTradeLog = TA_ListRemoveHead( entryListToUse );
        if( !entryTradeLog )
            return TA_ENTRY_TRANSACTION_MISSING;

        quantity = newTransaction->quantity;
        while( quantity )
        {
            entryTradeQuantity = -entryTradeLog->u.trade.quantity;
            if( entryTradeQuantity == quantity )
            {
                /* This entry have exactly the right amount of
                 * position for what needs to be closed.
                 * Just transform the entry into a trade.
                 */
                entryTradeLog->u.trade.quantity = quantity;
                entryTradeLog->u.trade.id = id;
                TA_TimestampCopy( &entryTradeLog->u.trade.exitTimestamp,
                                  &newTransaction->timestamp );
                /* Calculate the profit and make the entryPrice
                 * negative if this is a short trade.
                 * Both are multiplied by the quantity being
                 * traded.
                 */
                entryPrice = entryTradeLog->u.entry.entryPrice;
                if( newTransaction->type == TA_LONG_EXIT )
                {
                    entryTradeLog->u.trade.profit = (newTransaction->price-entryPrice)*quantity;
                    CALC_EXCURSION_LONG;
                }
                else
                {
                    entryTradeLog->u.trade.profit = (entryPrice-newTransaction->price)*quantity;
                    entryPrice = -entryPrice;
                    CALC_EXCURSION_SHORT;
                }
                entryTradeLog->u.entry.entryPrice = entryPrice * quantity;

                return TA_SUCCESS; /* Done! */
            }
            else if( entryTradeQuantity < quantity )
            {
                /* This entry have less than the amount of
                 * position that needs to be closed.
                 * Just transform the entry into a trade
                 * and move to the next entry.
                 */
                entryTradeLog->u.trade.quantity = entryTradeQuantity;
                quantity -= entryTradeQuantity;
                entryTradeLog->u.trade.id = id;
                TA_TimestampCopy( &entryTradeLog->u.trade.exitTimestamp,
                                  &newTransaction->timestamp );
                /* Calculate the profit and make the entryPrice
                 * negative if this is a short trade.
                 * Both are multiplied by the quantity being
                 * traded.
                 */
                entryPrice = entryTradeLog->u.trade.entryPrice;
                if( newTransaction->type == TA_LONG_EXIT )
                {
                    entryTradeLog->u.trade.profit = (newTransaction->price-entryPrice)*entryTradeQuantity;
                    CALC_EXCURSION_LONG;
                }
                else
                {
                    entryTradeLog->u.trade.profit = (entryPrice-newTransaction->price)*entryTradeQuantity;
                    entryPrice = -entryPrice;
                    CALC_EXCURSION_SHORT;
                }
                entryTradeLog->u.trade.entryPrice = entryPrice * entryTradeQuantity;

                /* Move to the next entry. If none available, that means there
                 * was more "exit" than "entry" and this is considered an
                 * error.
                 */
                entryTradeLog = TA_ListRemoveHead( entryListToUse );
                if( !entryTradeLog )
                    return TA_ENTRY_TRANSACTION_MISSING;
            }
            else
            {
                /* This entry have more position than what the
                 * exit requires, so the entry must be preserved.
                 * Consequently, a new tradeLog must be allocated.
                 */
                dataLog = TA_AllocatorForDataLog_Alloc( &tradeLogPriv->allocator );
                if( !dataLog )
                    return TA_ALLOC_ERR;

                TA_TimestampCopy( &dataLog->u.trade.entryTimestamp,
                                  &entryTradeLog->u.trade.entryTimestamp );
                TA_TimestampCopy( &dataLog->u.trade.exitTimestamp,
                                  &newTransaction->timestamp );
                dataLog->u.trade.quantity = quantity;
                entryPrice = entryTradeLog->u.trade.entryPrice;
                if( newTransaction->type == TA_LONG_EXIT )
                {
                    dataLog->u.trade.profit = (newTransaction->price-entryPrice)*quantity;
                    CALC_EXCURSION_LONG;
                }
                else
                {
                    dataLog->u.trade.profit = (entryPrice-newTransaction->price)*quantity;
                    entryPrice = -entryPrice;
                    CALC_EXCURSION_SHORT;
                }
                dataLog->u.trade.entryPrice = entryPrice*quantity;
                dataLog->u.trade.id = id;

                /* Adjust the entry and put it back for being process
                 * again later.
                 */
                entryTradeLog->u.trade.quantity += quantity;
                TA_ListNodeAddHead( entryListToUse, &entryTradeLog->u.entry.node, entryTradeLog );
                return TA_SUCCESS; /* Done! */
            }
        }
        break;
    default:
        return TA_INTERNAL_ERROR(121);
    }

    return TA_SUCCESS;
}
Пример #6
0
static ErrorNumber test_onetrade_only( 
                                     TA_KEY_TYPE keyTypeTest,
									          TA_TransactionType transactionType, 
                                     unsigned int winningTrade )
{
   TA_RetCode retCode;
   TA_Instrument  instrument;
   TA_Transaction transaction;
   TA_TradeLog   *tradeLog;
   TA_PM         *allocatedPM;
   ErrorNumber errorNumber;

   /* Allocate a TA_TradeLog. */
   retCode = TA_TradeLogAlloc( &tradeLog );
   if( (retCode != TA_SUCCESS) || (tradeLog == NULL) )
   {
      printRetCode( retCode );
      printf( "Failed: TA_TradeLogAlloc bad retCode! [%d]\n", retCode );
      return TA_PM_EMPTY_TA_TRADE_LOG_TESTS_0;
   }

   /* Add the entry TA_Transaction */
   switch( keyTypeTest )
   {
   case TA_KEY_TYPE_INTEGER:
      TA_InstrumentInitWithUserKey( &instrument, 0 );
      break;
   case TA_KEY_TYPE_CAT:
      TA_InstrumentInit( &instrument, "CATONLY", "" );
      break;
   case TA_KEY_TYPE_SYM:
      TA_InstrumentInit( &instrument, "", "SYMONLY" );
      break;
   case TA_KEY_TYPE_CATSYM:
      TA_InstrumentInit( &instrument, "C", "S" );
      break;
   default:
      return TA_PM_ERR_INVALID_KEY_TYPE;
   }

   transaction.id        = &instrument;
   transaction.price     = 10.00;
   transaction.quantity  = 1234;
   TA_TimestampCopy( &transaction.timestamp, &timestampNowPlusOneYear );
   transaction.type      = transactionType;

   retCode = TA_TradeLogAdd( tradeLog, &transaction );
   if( retCode != TA_SUCCESS )
   {
      printf( "Failed: TA_TradeLogAdd bad retCode %d\n", retCode );
      return TA_PM_2TRADETST_TRADELOGADD_1;
   }

   /* Set the corresponsing exit transaction type.
    * Also make the exit price either winning
    * or loosing.
    */
   if( transactionType == TA_LONG_ENTRY )
   {
      transaction.type = TA_LONG_EXIT;
      if( winningTrade )
         transaction.price = 12.00;
      else
         transaction.price = 9.50;
   }
   else if( transactionType == TA_SHORT_ENTRY )
   {
      transaction.type = TA_SHORT_EXIT;
      if( winningTrade )
         transaction.price = 9.25;
      else
         transaction.price = 11.00;
   }
   else
      return TA_PM_2TRADETST_BAD_TRADE_TYPE;

   /* Add the exit transaction. */
   retCode = TA_TradeLogAdd( tradeLog, &transaction );
   if( retCode != TA_SUCCESS )
   {
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_TradeLogAdd bad retCode %d\n", retCode );
      return TA_PM_2TRADETST_TRADELOGADD_2;
   }

   /* Create a TA_PM */
   retCode = TA_PMAlloc(
                         &timestampNow, &timestampNowPlusOneYear,
                         1000, &allocatedPM );
   if( retCode != TA_SUCCESS )
   {
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_PMAlloc bad retCode %d\n", retCode );
      return TA_PM_2TRADETST_PMALLOC_FAILED;
   }                    
   retCode = TA_PMAddTradeLog( allocatedPM, tradeLog );
   if( retCode != TA_SUCCESS )
   {
      TA_PMFree( allocatedPM );
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_PMAddTradeLog bad retCode %d\n", retCode );
      return TA_PM_2TRADETST_PMADDTRADELOG_FAILED;
   }                    

   /* Verify the NB of TRADE and the net profit */
   if( transactionType == TA_LONG_ENTRY )
   {
      if( winningTrade )
         errorNumber = checkPMvalues( allocatedPM, 1, 0, 2468, 0 );
      else
         errorNumber = checkPMvalues( allocatedPM, 1, 0, -617, 0 );
   }
   else if( transactionType == TA_SHORT_ENTRY )
   {
      if( winningTrade )
         errorNumber = checkPMvalues( allocatedPM, 0, 1, 0, 925.5 );
      else
         errorNumber = checkPMvalues( allocatedPM, 0, 1, 0, -1234);
   }
   else
      errorNumber = TA_PM_UNKNOWN_TRANSACTION_TYPE;

   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   errorNumber = checkNoHang( allocatedPM );
   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   /* Building a report should work */
   errorNumber = test_report( allocatedPM, 0 );
   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   /* Clean-up and exit */
   retCode = TA_PMFree( allocatedPM );
   if( retCode != TA_SUCCESS )
   {
      TA_PMFree( allocatedPM );
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_PMFree bad retCode %d\n", retCode );
      return TA_PM_2TRADETST_PMFREE_FAILED;
   }                    

   retCode = TA_TradeLogFree( tradeLog );
   if( retCode != TA_SUCCESS )
   {
      printRetCode( retCode );
      printf( "Failed: TA_TradeLogFree bad retCode %d\n", retCode );
      return TA_PM_TRADELOGFREE_ONE_TRADE_FAILED;      
   }

   return TA_TEST_PASS;
}
Пример #7
0
static ErrorNumber test_onetransaction_only( TA_KEY_TYPE keyTypeTest )
{
   TA_RetCode retCode;
   TA_Instrument  instrument;
   TA_Transaction       transaction;
   TA_TradeLog   *tradeLog;
   ErrorNumber errorNumber;
   TA_PM *allocatedPM;

   /* Allocate an empty TA_TradeLog. */
   retCode = TA_TradeLogAlloc( &tradeLog );
   if( (retCode != TA_SUCCESS) || (tradeLog == NULL) )
   {
      printRetCode( retCode );
      printf( "Failed: TA_TradeLogAlloc bad retCode! [%d]\n", retCode );
      return TA_PM_EMPTY_TA_TRADE_LOG_TESTS_0;
   }

   /* Add one TA_Transaction */
   switch( keyTypeTest )
   {
   case TA_KEY_TYPE_INTEGER:
      TA_InstrumentInitWithUserKey( &instrument, 0x12345432 );
      break;
   case TA_KEY_TYPE_CAT:
      TA_InstrumentInit( &instrument, "CATONLY", NULL );
      break;
   case TA_KEY_TYPE_SYM:
      TA_InstrumentInit( &instrument, NULL, "S" );
      break;
   case TA_KEY_TYPE_CATSYM:
      TA_InstrumentInit( &instrument, "CATABCDEFGHIJKLMNOPQRSTUVWXYZ", "SYM012345678901234567890" );
      break;
   default:
      return TA_PM_ERR_INVALID_KEY_TYPE;
   }

   transaction.id        = &instrument;
   transaction.price     = 34.45;
   transaction.quantity  = 8765;
   TA_TimestampCopy( &transaction.timestamp, &timestampNow );
   transaction.type      = TA_LONG_ENTRY;

   retCode = TA_TradeLogAdd( tradeLog, &transaction );
   if( retCode != TA_SUCCESS )
   {
      printRetCode( retCode );
      printf( "Failed: TA_TradeLogAdd bad retCode %d\n", retCode );
      return TA_PM_TRADELOGADD_ONE_TRADE_FAILED;
   }

   /* Create a TA_PM */
   retCode = TA_PMAlloc(
                         &timestampNow,
                         &timestampNow, 
                         1000, &allocatedPM );
   if( retCode != TA_SUCCESS )
   {
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_PMAlloc bad retCode %d\n", retCode );
      return TA_PM_TRADELOGADD_ONE_TRADE_FAILED_1;
   }                    
   retCode = TA_PMAddTradeLog( allocatedPM, tradeLog );
   if( retCode != TA_SUCCESS )
   {
      TA_PMFree( allocatedPM );
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_PMAddTradeLog bad retCode %d\n", retCode );
      return TA_PM_TRADELOGADD_ONE_TRADE_FAILED_2;
   }                    

   /* Verify the NB of TRADE */
   errorNumber = checkPMvalues( allocatedPM, 0, 0, 0, 0 );

   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   errorNumber = checkNoHang( allocatedPM );
   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   /* Building a report should work */
   errorNumber = test_report( allocatedPM, 0 );
   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   /* Clean-up and exit */
   retCode = TA_PMFree( allocatedPM );
   if( retCode != TA_SUCCESS )
   {
      TA_PMFree( allocatedPM );
      TA_TradeLogFree( tradeLog );
      printRetCode( retCode );
      printf( "Failed: TA_PMFree bad retCode %d\n", retCode );
      return TA_PM_TRADELOGADD_ONE_TRADE_FAILED_3;
   }                    


   retCode = TA_TradeLogFree( tradeLog );
   if( retCode != TA_SUCCESS )
   {
      printRetCode( retCode );
      printf( "Failed: TA_TradeLogFree bad retCode %d\n", retCode );
      return TA_PM_TRADELOGFREE_ONE_TRADE_FAILED;      
   }

   return TA_TEST_PASS;
}
Пример #8
0
/**** Global functions definitions.   ****/
ErrorNumber test_pm( void )
{
   ErrorNumber errorNumber;
   TA_UDBase *udb;
   unsigned int i, j;

   printf( "Testing Performance Measurement\n" );

   /* Side Note:
    * Why all these allocLib/freeLib in this function?
    *   Each time freeLib is being called, it is verified
    *   that all ressource has been freed. So that's a good
    *   way to verify for any potential memory leak.
    */

   /* Initialize some globals used throughout these tests. */
   TA_SetTimeNow( &timestampNow );
   TA_SetDateNow( &timestampNow );
   TA_NextWeekday( &timestampNow );
   TA_TimestampCopy( &timestampNowPlusOneYear, &timestampNow );
   TA_NextYear( &timestampNowPlusOneYear );
   TA_PrevDay( &timestampNowPlusOneYear );

   /* Using a user defined kkey */
   TA_InstrumentInitWithUserKey( &id1_1, 12 );
   TA_InstrumentInitWithUserKey( &id1_2,  9 );

   /* Using a category / symbol strings. */
   TA_InstrumentInit( &id2_1, "AB", "CD" );
   TA_InstrumentInit( &id2_1, "AB", "CE" );

   /* Using a category only. */
   TA_InstrumentInit( &id3_1, "ABCD", NULL );
   TA_InstrumentInit( &id3_2, "EFGH", NULL );

   /* Using only a symbol string */
   TA_InstrumentInit( &id4_1, NULL, "A" );
   TA_InstrumentInit( &id4_2, NULL, "B" );

   /* Test limit cases with empty TA_TradeLog */
   errorNumber = allocLib( &udb );
   if( errorNumber != TA_TEST_PASS )
      return errorNumber;    
   errorNumber = test_emptytradelog();
   if( errorNumber != TA_TEST_PASS )
   {
      printf( "Failed: Empty trade log cases\n" );   
      return errorNumber;
   }
   errorNumber = freeLib( udb );
   if( errorNumber != TA_TEST_PASS )
      return errorNumber;

   /* Test with only one TA_Transaction.
    * Repeat all tests for each possible
    * TA_Instrument key type.
    */
   for( i=0; i < NB_TA_KEY_TYPE; i++ )
   {
      errorNumber = allocLib( &udb );
      if( errorNumber != TA_TEST_PASS )
         return errorNumber;    
      errorNumber = test_onetransaction_only( (TA_KEY_TYPE)i );
      if( errorNumber != TA_TEST_PASS )
      {
         printf( "Failed: one transaction cases (key=%d,errorNumber=%d)\n", (TA_KEY_TYPE)i, errorNumber );
         return errorNumber;
      }
      errorNumber = freeLib( udb );
      if( errorNumber != TA_TEST_PASS )
         return errorNumber;
   }

   /* Tests with two TA_Transaction for the
    * same given TA_Instrument.
    *
    * Repeat the test for each combination
    * of:
    *  - TA_Instrument key type
    *  - long and short trade.
    *  - winning and losing trade
    */
   for( i=0; i <= 1; i++ )
   {
      /* 0 = test a loosing trade
       * 1 = test a winning trade
       */   
      for( j=0; j < NB_TA_KEY_TYPE; j++ )
      {
         /* Test Long */
         errorNumber = allocLib( &udb );
         if( errorNumber != TA_TEST_PASS )
            return errorNumber;    
         errorNumber = test_onetrade_only( (TA_KEY_TYPE)j, TA_LONG_ENTRY, i );
         if( errorNumber != TA_TEST_PASS )
         {
            printf( "Failed: one trade only (key=%d,type=%d,winning=%d)\n", (TA_KEY_TYPE)j, TA_LONG_ENTRY, i );
            return errorNumber;
         }
         errorNumber = freeLib( udb );
         if( errorNumber != TA_TEST_PASS )
            return errorNumber;

         /* Test Short */
         errorNumber = allocLib( &udb );
         if( errorNumber != TA_TEST_PASS )
            return errorNumber;    
         errorNumber = test_onetrade_only( (TA_KEY_TYPE)j, TA_SHORT_ENTRY, i );
         if( errorNumber != TA_TEST_PASS )
         {
            printf( "Failed: one trade only (key=%d,type=%d,winning=%d)\n", (TA_KEY_TYPE)j, TA_SHORT_ENTRY, i );
            return errorNumber;
         }
         errorNumber = freeLib( udb );
         if( errorNumber != TA_TEST_PASS )
            return errorNumber;
      }
   }

   /* Test TA_PMValueId using a list of tests
    * defined in static variables.
    */
   for( i=0; i < NB_PMVALUEID_TEST; i++ )
   {
      errorNumber = allocLib( &udb );
      if( errorNumber != TA_TEST_PASS )
         return errorNumber;
      errorNumber = test_valueId( &pmValueIdTests[i] );
      if( errorNumber != TA_TEST_PASS )
      {
         printf( "Failed: test_valueId #%d\n", i);
         return errorNumber;
      }
      errorNumber = freeLib( udb );
      if( errorNumber != TA_TEST_PASS )
         return errorNumber;
   }

   /* Test TA_PMArrayId using a list of tests
    * defined in static variables.
    */
   for( i=0; i < NB_PMARRAYID_TEST; i++ )
   {
      errorNumber = allocLib( &udb );
      if( errorNumber != TA_TEST_PASS )
         return errorNumber;
      errorNumber = test_arrayId( &pmArrayIdTests[i] );
      if( errorNumber != TA_TEST_PASS )
      {
         printf( "Failed: test_arrayId #%d\n", i);
         return errorNumber;
      }
      errorNumber = freeLib( udb );
      if( errorNumber != TA_TEST_PASS )
         return errorNumber;
   }

   return TA_TEST_PASS;
}
Пример #9
0
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;
}