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; }
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; }
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; }