unsigned int TA_FileIndexIdentifyFileDepth( TA_FileIndexPriv *data ) { TA_List *listLocationToken; TA_TokenInfo *info; unsigned int latestPathPortionDepth; unsigned int currentDepth; /* Identify the last portion of the path where * the directory path is all resolved. * * It is imperative that the last token be TA_TOK_END. */ listLocationToken = data->listLocationToken; info = TA_ListAccessHead( listLocationToken ); latestPathPortionDepth = 1; currentDepth = 0; while( info ) { currentDepth++; if( info->id == TA_TOK_END ) return latestPathPortionDepth; if( info->id == TA_TOK_SEP ) latestPathPortionDepth = currentDepth; info = TA_ListAccessNext( listLocationToken ); } /* Should never get here. */ TA_FATAL_RET( data->libHandle, NULL, currentDepth, latestPathPortionDepth, 0 ); }
TA_RetCode TA_PMFree( TA_PM *toBeFreed ) { TA_PMPriv *pmPriv; TA_TradeLogPriv *tradeLogPriv; if( toBeFreed ) { /* Make sure this is a valid object */ pmPriv = (TA_PMPriv *)toBeFreed->hiddenData; if( !pmPriv || (pmPriv->magicNb != TA_PMPRIV_MAGIC_NB) ) return TA_BAD_OBJECT; /* Clearly mark this object as being unusable. */ pmPriv->magicNb = 0; /* Indicate to all TA_TradeLog that they do * no belong to this TA_PM anymore. */ tradeLogPriv = TA_ListAccessHead( &pmPriv->tradeLogList ); while( tradeLogPriv ) { tradeLogPriv->nbReferenceFromTA_PM--; tradeLogPriv = TA_ListAccessNext( &pmPriv->tradeLogList ); } TA_ListFree( &pmPriv->tradeLogList ); /* Free all the cached arrays. */ FREE_IF_NOT_NULL( pmPriv->equity ); FREE_IF_NOT_NULL( pmPriv->arrayTimestamp ); FREE_IF_NOT_NULL( pmPriv->shortArrayCache.investment ); FREE_IF_NOT_NULL( pmPriv->shortArrayCache.profit ); FREE_IF_NOT_NULL( pmPriv->longArrayCache.investment ); FREE_IF_NOT_NULL( pmPriv->longArrayCache.profit ); FREE_IF_NOT_NULL( pmPriv->totalArrayCache.investment ); FREE_IF_NOT_NULL( pmPriv->totalArrayCache.profit ); /* Last thing that must be freed... */ TA_Free( toBeFreed ); } return TA_SUCCESS; }
TA_RetCode TA_PMAddTradeLog( TA_PM *pm, TA_TradeLog *tradeLogToAdd ) { TA_TradeLogPriv *tradeLogPriv; TA_TradeLogPriv *tradeLogPrivIter; TA_PMPriv *pmPriv; TA_RetCode retCode; if( !pm || !tradeLogToAdd ) return TA_BAD_PARAM; /* Make sure this TA_PM is a valid object */ pmPriv = (TA_PMPriv *)pm->hiddenData; if( !pmPriv || (pmPriv->magicNb != TA_PMPRIV_MAGIC_NB) ) return TA_BAD_OBJECT; /* Make sure this TA_TradeLog is a valid object. */ tradeLogPriv = (TA_TradeLogPriv *)tradeLogToAdd->hiddenData; if( !tradeLogPriv || (tradeLogPriv->magicNb != TA_TRADELOGPRIV_MAGIC_NB) ) return TA_BAD_OBJECT; /* Make sure it was not already added */ tradeLogPrivIter = TA_ListAccessHead( &pmPriv->tradeLogList ); while( tradeLogPrivIter ) { if( tradeLogPrivIter == tradeLogPriv ) return TA_TRADELOG_ALREADY_ADDED; tradeLogPrivIter = TA_ListAccessNext( &pmPriv->tradeLogList ); } /* Add it to the list and bump the reference count of the TA_TradeLog */ retCode = TA_ListAddTail( &pmPriv->tradeLogList, tradeLogPriv ); if( retCode != TA_SUCCESS ) return retCode; tradeLogPriv->nbReferenceFromTA_PM++; /* Invalidate cached calculation. */ pmPriv->flags &= ~TA_PMVALUECACHE_CALCULATED; return TA_SUCCESS; }
/* Two very limited function to walk up/down in the Tree. * These functions are useful only when walking on a known linear portion * of the tree (when for each parent there is only one child). */ TA_ValueTreeNode *TA_FileIndexGoDownTreeValue( TA_FileIndexPriv *data ) { TA_ValueTreeNode *retValue; TA_Libc *libHandle; libHandle = data->libHandle; TA_ASSERT_RET( libHandle, data != NULL, (TA_ValueTreeNode *)NULL ); /* Go down using the first child only. */ if( (!data->currentNode) || (!data->currentNode->child) ) return NULL; retValue = TA_ListAccessHead( data->currentNode->child ); /* Change data->currentNode only if we really can go down... */ if( retValue ) data->currentNode = retValue; return retValue; }
/**** Global functions definitions. ****/ TA_RetCode TA_PMValue( TA_PM *pm, TA_PMValueId valueId, TA_PMGroup grp, TA_Real *value ) { TA_PMPriv *pmPriv; TA_RetCode retCode; TA_List *tradeLogList; TA_TradeLogPriv *tradeLogPriv; unsigned int tempInt, tempInt2; TA_Real tempReal, tempReal2, tempReal3; /* Validate parameters. */ if( !pm || !value ) return TA_BAD_PARAM; if( (valueId >= TA_PM_NB_VALUEID ) || (grp >= TA_PM_NB_GROUP ) ) return TA_BAD_PARAM; /* Make sure 'pm' is a ptr on a valid object */ pmPriv = (TA_PMPriv *)pm->hiddenData; if( !pmPriv || (pmPriv->magicNb != TA_PMPRIV_MAGIC_NB) ) return TA_BAD_OBJECT; /* Process ALL the basic calculation if not already done. */ tradeLogList = &pmPriv->tradeLogList; tradeLogPriv = TA_ListAccessHead( tradeLogList ); if( !(pmPriv->flags & TA_PMVALUECACHE_CALCULATED) ) { initValueCache( &pmPriv->longValueCache ); initValueCache( &pmPriv->shortValueCache ); if( !tradeLogPriv ) return TA_NO_TRADE_LOG; else { do { if( !(tradeLogPriv->flags & TA_PMVALUECACHE_CALCULATED) ) { retCode = processTradeLog_BasicCalculation( &pmPriv->startDate, &pmPriv->endDate, tradeLogPriv ); } mergeValueCache( &pmPriv->longValueCache, &tradeLogPriv->longValueCache ); mergeValueCache( &pmPriv->shortValueCache, &tradeLogPriv->shortValueCache ); tradeLogPriv = TA_ListAccessNext( tradeLogList ); } while( tradeLogPriv ); } pmPriv->flags |= TA_PMVALUECACHE_CALCULATED; } switch( valueId ) { case TA_PM_TOTAL_NB_OF_TRADE: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->shortValueCache.nbLosingTrade; break; default: return TA_BAD_PARAM; } *value = (TA_Real)tempInt; break; case TA_PM_NB_WINNING_TRADE: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; break; default: return TA_BAD_PARAM; } *value = (TA_Real)tempInt; break; case TA_PM_NB_LOSING_TRADE: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbLosingTrade; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; break; default: return TA_BAD_PARAM; } *value = (TA_Real)tempInt; break; case TA_PM_TOTAL_NET_PROFIT: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->shortValueCache.sumLoss; /* loss are negative */ tempReal += pmPriv->longValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->longValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->shortValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } if( tempInt == 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal; break; case TA_PM_STARTING_CAPITAL: *value = pmPriv->initialCapital; switch( grp ) { case TA_PM_ALL_TRADES: break; case TA_PM_LONG_TRADES: case TA_PM_SHORT_TRADES: return TA_VALUE_NOT_APPLICABLE; default: return TA_BAD_PARAM; } break; case TA_PM_ENDING_CAPITAL: tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->shortValueCache.sumLoss; /* loss are negative */ tempReal += pmPriv->longValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumLoss; *value = pmPriv->initialCapital + tempReal; switch( grp ) { case TA_PM_ALL_TRADES: break; case TA_PM_LONG_TRADES: case TA_PM_SHORT_TRADES: return TA_VALUE_NOT_APPLICABLE; default: return TA_BAD_PARAM; } break; case TA_PM_GROSS_PROFIT: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumProfit; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; tempReal = pmPriv->longValueCache.sumProfit; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempReal = pmPriv->shortValueCache.sumProfit; break; default: return TA_BAD_PARAM; } if( tempInt == 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal; break; case TA_PM_GROSS_LOSS: /* loss are negative */ switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumLoss; tempReal += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } if( tempInt == 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal; break; case TA_PM_PROFIT_FACTOR: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; tempReal2 += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->shortValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } if( (tempReal2 >= 0.0) || (tempReal <= 0.0) ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal/(-tempReal2); break; case TA_PM_AVG_PROFIT: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumProfit; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; tempReal = pmPriv->longValueCache.sumProfit; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempReal = pmPriv->shortValueCache.sumProfit; break; default: return TA_BAD_PARAM; } if( tempInt <= 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal/((TA_Real)tempInt); break; case TA_PM_PERCENT_PROFITABLE: switch( grp ) { case TA_PM_ALL_TRADES: tempInt2 = pmPriv->shortValueCache.nbWinningTrade; tempInt2 += pmPriv->longValueCache.nbWinningTrade; tempInt = pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; break; case TA_PM_LONG_TRADES: tempInt2 = pmPriv->longValueCache.nbWinningTrade; tempInt = pmPriv->longValueCache.nbLosingTrade; break; case TA_PM_SHORT_TRADES: tempInt2 = pmPriv->shortValueCache.nbWinningTrade; tempInt = pmPriv->shortValueCache.nbLosingTrade; break; default: return TA_BAD_PARAM; } if( tempInt == 0 ) { /* No losing trades. */ if( tempInt2 == 0 ) { /* No trades at all */ *value = (TA_Real)0.0; return TA_VALUE_NOT_APPLICABLE; } /* All trades are winning trades. */ *value = (TA_Real)100.0; } else { tempInt += tempInt2; *value = ((TA_Real)tempInt2)/((TA_Real)tempInt)*100.0; } break; case TA_PM_AVG_PROFIT_PERCENT: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempInt += pmPriv->longValueCache.nbWinningTrade; tempReal = pmPriv->shortValueCache.sumProfitPercent; tempReal += pmPriv->longValueCache.sumProfitPercent; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbWinningTrade; tempReal = pmPriv->longValueCache.sumProfitPercent; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbWinningTrade; tempReal = pmPriv->shortValueCache.sumProfitPercent; break; default: return TA_BAD_PARAM; } if( tempInt <= 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = (tempReal*100.0)/((TA_Real)tempInt); break; case TA_PM_AVG_LOSS: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumLoss; tempReal += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } if( tempInt <= 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal/((TA_Real)tempInt); break; case TA_PM_AVG_LOSS_PERCENT: switch( grp ) { case TA_PM_ALL_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempInt += pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumLossPercent; tempReal += pmPriv->longValueCache.sumLossPercent; break; case TA_PM_LONG_TRADES: tempInt = pmPriv->longValueCache.nbLosingTrade; tempReal = pmPriv->longValueCache.sumLossPercent; break; case TA_PM_SHORT_TRADES: tempInt = pmPriv->shortValueCache.nbLosingTrade; tempReal = pmPriv->shortValueCache.sumLossPercent; break; default: return TA_BAD_PARAM; } if( tempInt <= 0 ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = (tempReal*100.0)/((TA_Real)tempInt); break; case TA_PM_LARGEST_PROFIT: switch( grp ) { case TA_PM_ALL_TRADES: tempReal = MAX( pmPriv->shortValueCache.largestProfit, pmPriv->longValueCache.largestProfit ); break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.largestProfit; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.largestProfit; break; default: return TA_BAD_PARAM; } if( tempReal == TA_REAL_MIN ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal; break; case TA_PM_LARGEST_LOSS: switch( grp ) { case TA_PM_ALL_TRADES: tempReal = MIN( pmPriv->shortValueCache.largestLoss, pmPriv->longValueCache.largestLoss ); break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.largestLoss; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.largestLoss; break; default: return TA_BAD_PARAM; } if( tempReal == TA_REAL_MAX ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal; break; case TA_PM_LARGEST_PROFIT_PERCENT: switch( grp ) { case TA_PM_ALL_TRADES: tempReal = MAX( pmPriv->shortValueCache.largestProfitPercent, pmPriv->longValueCache.largestProfitPercent ); break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.largestProfitPercent; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.largestProfitPercent; break; default: return TA_BAD_PARAM; } if( tempReal == TA_REAL_MIN ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal*100.0; break; case TA_PM_LARGEST_LOSS_PERCENT: switch( grp ) { case TA_PM_ALL_TRADES: tempReal = MIN( pmPriv->shortValueCache.largestLossPercent, pmPriv->longValueCache.largestLossPercent ); break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.largestLossPercent; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.largestLossPercent; break; default: return TA_BAD_PARAM; } if( tempReal == TA_REAL_MAX ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = tempReal*100.0; break; case TA_PM_RATE_OF_RETURN: /* One-period rate of return: (profit / initialCapital) */ tempReal3 = pmPriv->initialCapital; if( tempReal3 <= 0.0 ) { *value = 0.0; return TA_BAD_STARTING_CAPITAL; } switch( grp ) { case TA_PM_ALL_TRADES: tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; /* loss are negative */ tempReal2 += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } if( (tempReal <= 0.0) && (tempReal2 >= 0.0) ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else *value = ((tempReal+tempReal2)/tempReal3)*100.0; break; case TA_PM_ANNUALIZED_RETURN: /* Annualized rate of return on a simple-interest basis: * * (Ending Value - Starting Value) 365 * ------------------------------- * --- * Starting Value n * * Where 'n' is the number of day between the end and * start date when the TA_PM was created. */ tempReal3 = pmPriv->initialCapital; if( tempReal3 <= 0.0 ) { *value = 0.0; return TA_BAD_STARTING_CAPITAL; } switch( grp ) { case TA_PM_ALL_TRADES: tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; /* loss are negative */ tempReal2 += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } retCode = TA_TimestampDeltaDay( &pmPriv->startDate, &pmPriv->endDate, &tempInt ); if( retCode != TA_SUCCESS ) return retCode; if( ((tempReal <= 0.0) && (tempReal2 >= 0.0)) || (tempInt == 0) ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else { tempReal += tempReal2; *value = (tempReal/tempReal3)*(36500.0/tempInt); } break; case TA_PM_ANNUALIZED_COMPOUNDED_RETURN: /* Annualized compounded rate of return: * * ((Ending Value / Starting Value)^(1/y)) - 1 * * Where 'y' is the number of year between the end * and start date when the TA_PM was created. */ tempReal3 = pmPriv->initialCapital; if( tempReal3 <= 0.0 ) { *value = 0.0; return TA_BAD_STARTING_CAPITAL; } switch( grp ) { case TA_PM_ALL_TRADES: tempReal = pmPriv->shortValueCache.sumProfit; tempReal += pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; /* loss are negative */ tempReal2 += pmPriv->longValueCache.sumLoss; break; case TA_PM_LONG_TRADES: tempReal = pmPriv->longValueCache.sumProfit; tempReal2 = pmPriv->longValueCache.sumLoss; break; case TA_PM_SHORT_TRADES: tempReal = pmPriv->shortValueCache.sumProfit; tempReal2 = pmPriv->shortValueCache.sumLoss; break; default: return TA_BAD_PARAM; } retCode = TA_TimestampDeltaDay( &pmPriv->startDate, &pmPriv->endDate, &tempInt ); if( retCode != TA_SUCCESS ) return retCode; if( ((tempReal <= 0.0) && (tempReal2 >= 0.0)) || (tempInt == 0) ) { *value = 0.0; return TA_VALUE_NOT_APPLICABLE; } else { tempReal += (tempReal2+tempReal3); *value = (pow(tempReal/tempReal3,1.0/(tempInt/365.0)) - 1.0)*100.0; } break; default: return TA_INVALID_VALUE_ID; } return TA_SUCCESS; }
/* Perform the very first level of calculation among all * the trades allocated. * * Note: A trade is a TA_DataLog with the quantity > 0 */ static TA_RetCode processTradeLog_BasicCalculation( TA_Timestamp *startDate, TA_Timestamp *endDate, TA_TradeLogPriv *tradeLog ) { TA_AllocatorForDataLog *allocator; TA_DataLogBlock *block; TA_List *list; TA_DataLog *invalidDataLog, *curDataLog; int i; TA_PMValueCache *shortValueCache, *longValueCache; /* Temporary values for calculation. */ register TA_Real tempReal1, tempReal2, tempReal3; register int tempInt1; /* The following variables are all the * accumulators. * * Some are suggested to be kept in * registers, most of the others will * be maintain within the local TA_PMValueCache. * * All these value are then merge within the * tradeLog at the very end. */ register int long_nbLosingTrade; register int short_nbLosingTrade; register int long_nbWinningTrade; register int short_nbWinningTrade; TA_PMValueCache shortV, longV; /* Initialize all accumulators. */ initValueCache( &shortV ); initValueCache( &longV ); long_nbLosingTrade = short_nbLosingTrade = long_nbWinningTrade = short_nbWinningTrade = 0; /* Simply iterate through all the TA_TradeLog * and update the accumulators. */ allocator = &tradeLog->allocator; if( allocator ) { list = &allocator->listOfDataLogBlock; block = TA_ListAccessHead( list ); while( block ) { /* Process each blocks. */ invalidDataLog = allocator->nextAvailableTrade; curDataLog = block->array; for( i=0; i < TA_TRADE_BLOCK_SIZE; i++ ) { if( curDataLog == invalidDataLog ) { break; } else { /* Process each TA_DataLog being * a trade (not an entry) * An entry have a negative 'quantity'. */ tempInt1 = curDataLog->u.trade.quantity; if( (tempInt1 > 0) && !TA_TimestampLess( &curDataLog->u.trade.entryTimestamp, startDate ) && !TA_TimestampGreater( &curDataLog->u.trade.exitTimestamp, endDate ) ) { tempReal1 = curDataLog->u.trade.entryPrice; /* Positive = long, negative = short */ tempReal2 = curDataLog->u.trade.profit; /* Positive = winning, negative = losing */ if( tempReal1 > 0.0 ) { /* This is a long trade. */ if( tempReal2 > 0.0 ) { /* This is a winning long trade */ longV.sumInvestmentProfit += tempReal1; longV.sumProfit += tempReal2; TA_SET_MAX(longV.largestProfit, tempReal2 ); tempReal1 = tempReal2/tempReal1; TA_SET_MAX(longV.largestProfitPercent, tempReal1 ); longV.sumProfitPercent += tempReal1; long_nbWinningTrade++; } else { /* This is a losing long trade */ longV.sumInvestmentLoss += tempReal1; longV.sumLoss += tempReal2; TA_SET_MIN(longV.largestLoss, tempReal2 ); tempReal1 = tempReal2/tempReal1; TA_SET_MIN(longV.largestLossPercent, tempReal1 ); longV.sumLossPercent += tempReal1; long_nbLosingTrade++; } } else { /* This is a short trade. */ if( tempReal2 > 0.0 ) { /* This is a winning short trade */ tempReal1 = -tempReal1; shortV.sumInvestmentProfit += tempReal1; shortV.sumProfit += tempReal2; TA_SET_MAX(shortV.largestProfit, tempReal2 ); tempReal1 = tempReal2/tempReal1; TA_SET_MAX(shortV.largestProfitPercent, tempReal1 ); shortV.sumProfitPercent += tempReal1; short_nbWinningTrade++; } else { /* This is a losing short trade */ tempReal1 = -tempReal1; shortV.sumInvestmentLoss += tempReal1; shortV.sumLoss += tempReal2; TA_SET_MIN(shortV.largestLoss, tempReal2 ); tempReal1 = tempReal2/tempReal1; TA_SET_MIN(shortV.largestLossPercent, tempReal1 ); shortV.sumLossPercent += tempReal1; short_nbLosingTrade++; } } } } curDataLog++; } block = TA_ListAccessNext( list ); } } /* Initialize the output with the accumulated results. */ shortValueCache = &tradeLog->shortValueCache; longValueCache = &tradeLog->longValueCache; *shortValueCache = shortV; *longValueCache = longV; shortValueCache->nbLosingTrade = short_nbLosingTrade; shortValueCache->nbWinningTrade = short_nbWinningTrade; longValueCache->nbLosingTrade = long_nbLosingTrade; longValueCache->nbWinningTrade = long_nbWinningTrade; /* Indicate that the value are now calculated and set * in the "cache". */ tradeLog->flags |= TA_PMVALUECACHE_CALCULATED; return TA_SUCCESS; }
TA_RetCode TA_TradeReportAlloc( TA_PM *pm, TA_TradeReport **tradeReportAllocated ) { TA_PMPriv *pmPriv; TA_TradeReport *tradeReport; TA_TradeReportPriv *tradeReportPriv; TA_List *tradeLogList; TA_TradeLogPriv *tradeLogPriv; TA_AllocatorForDataLog *allocator; TA_DataLogBlock *block; TA_List *listOfBlock; TA_DataLog *invalidDataLog; TA_DataLog *curDataLog; TA_Trade **tradePtr; TA_Timestamp *startDate; TA_Timestamp *endDate; TA_RetCode retCode; TA_Real tempReal; int nbTrade, nbTradeAdded, i; if( !tradeReportAllocated ) return TA_BAD_PARAM; *tradeReportAllocated = NULL; if( !pm ) return TA_BAD_PARAM; /* Make sure this TA_PM is a valid object */ pmPriv = (TA_PMPriv *)pm->hiddenData; if( !pmPriv || (pmPriv->magicNb != TA_PMPRIV_MAGIC_NB) ) return TA_BAD_OBJECT; tradeReport = TA_Malloc( sizeof( TA_TradeReport ) + sizeof( TA_TradeReportPriv ) ); if( !tradeReport ) return TA_ALLOC_ERR; memset( tradeReport, 0, sizeof( TA_TradeReport ) + sizeof( TA_TradeReportPriv ) ); tradeReportPriv = (TA_TradeReportPriv *)(((char *)tradeReport)+sizeof(TA_TradeReport)); tradeReportPriv->magicNb = TA_TRADEREPORT_MAGIC_NB; tradeReport->hiddenData = tradeReportPriv; /* TA_TradeReportFree can be safely called from this point. */ /* Get the number of closed trades */ tempReal = 0; retCode = TA_PMValue( pm, TA_PM_TOTAL_NB_OF_TRADE, TA_PM_ALL_TRADES, &tempReal ); if( retCode != TA_SUCCESS ) { TA_TradeReportFree( tradeReport ); return retCode; } nbTrade = (unsigned int)tempReal; tradeReport->nbTrades = nbTrade; if( nbTrade != 0 ) { startDate = &pmPriv->startDate; endDate = &pmPriv->endDate; tradePtr = (TA_Trade **)TA_Malloc( nbTrade*sizeof(const TA_Trade *)); tradeReport->trades = (const TA_Trade **)tradePtr; if( !tradePtr ) { TA_TradeReportFree( tradeReport ); return TA_ALLOC_ERR; } /* Iterate through all the closed trades. */ nbTradeAdded = 0; tradeLogList = &pmPriv->tradeLogList; tradeLogPriv = TA_ListAccessHead( tradeLogList ); if( !tradeLogPriv ) { TA_TradeReportFree( tradeReport ); return TA_NO_TRADE_LOG; } do { allocator = &tradeLogPriv->allocator; listOfBlock = &allocator->listOfDataLogBlock; block = TA_ListAccessHead( listOfBlock ); while( block ) { /* Process each blocks. */ invalidDataLog = allocator->nextAvailableTrade; curDataLog = block->array; for( i=0; i < TA_TRADE_BLOCK_SIZE; i++ ) { if( curDataLog == invalidDataLog ) { break; } else { /* Process each TA_DataLog being a trade (not an entry) * An entry have a negative 'quantity'. */ if( (curDataLog->u.trade.quantity > 0) && !TA_TimestampLess( &curDataLog->u.trade.entryTimestamp, startDate ) && !TA_TimestampGreater( &curDataLog->u.trade.exitTimestamp, endDate ) ) { /* Make sure not to exceed array size */ if( nbTradeAdded >= nbTrade ) { TA_TradeReportFree( tradeReport ); return TA_ALLOC_ERR; } tradePtr[nbTradeAdded++] = &curDataLog->u.trade; } } curDataLog++; } block = TA_ListAccessNext( listOfBlock ); } tradeLogPriv = TA_ListAccessNext( tradeLogList ); } while( tradeLogPriv ); /* Make sure all trades were initialized. */ if( nbTradeAdded != nbTrade ) { TA_TradeReportFree( tradeReport ); return TA_ALLOC_ERR; } /* Sort all trades in chronological order of exit. */ qsort( tradePtr, (size_t)nbTrade, sizeof(TA_Trade *), compareTrade ); } /* All succeed. Return pointer to caller. */ *tradeReportAllocated = tradeReport; return TA_SUCCESS; }
/**** Global functions definitions. ****/ TA_RetCode TA_PMArrayAlloc( TA_PM *pm, TA_PMArrayId arrayId, TA_PMGroup grp, TA_Period period, TA_PMArray **allocatedArray ) { TA_PMPriv *pmPriv; TA_List *tradeLogList; TA_TradeLogPriv *tradeLogPriv; int timeSerieSize; TA_RetCode retCode; TA_PMArray *newPMArray; unsigned int finalNbBars; TA_Real *finalData; TA_Timestamp *finalTimestamp; if( !allocatedArray ) return TA_BAD_PARAM; *allocatedArray = NULL; if( !pm || (arrayId >= TA_PM_NB_ARRAYID) || (grp >= TA_PM_NB_GROUP) ) return TA_BAD_PARAM; /* Make sure 'pm' is a ptr on a valid object */ pmPriv = (TA_PMPriv *)pm->hiddenData; if( pmPriv->magicNb != TA_PMPRIV_MAGIC_NB ) return TA_BAD_OBJECT; #if 0 /* Get the number of trade that applies to the period. * Doing so will also force the update of all * "basic calculation" if needed. */ retCode = TA_PMValue( pm, TA_PM_TOTAL_NB_OF_TRADE, TA_PM_ALL_TRADES, &nbTrade ); if( retCode != TA_SUCCESS ) return retCode; #endif /* Because the startDate/endDate are fix in the * lifetime of a TA_PM, all the cached time series * are allocated once here and freed only when the * TA_PM is freed. */ if( !pmPriv->arrayTimestamp ) { /* Allocate the timestamps (excluding week-end) * from [startDate..endDate] inclusive. * There is only one array of timestamps for all the * time series. */ pmPriv->arrayTimestamp = allocTimestampArray( &pmPriv->startDate, &pmPriv->endDate, (int *)&pmPriv->nbDailyBars ); if( !pmPriv->arrayTimestamp ) return TA_ALLOC_ERR; } if( !(pmPriv->flags & TA_PMARRAYCACHE_CALCULATED) ) { /* The cached time serie needs to be recalculated * from scratch. */ tradeLogList = &pmPriv->tradeLogList; tradeLogPriv = TA_ListAccessHead( tradeLogList ); if( !tradeLogPriv ) return TA_NO_TRADE_LOG; else { /* Make sure all required cached time series are correctly * allocated. */ timeSerieSize = sizeof(TA_Real)*pmPriv->nbDailyBars; #define TRY_ALLOC_IF_NULL(x) { \ if( !x ) \ { \ x = TA_Malloc( timeSerieSize ); \ if( !x ) \ return TA_ALLOC_ERR; \ } } TRY_ALLOC_IF_NULL( pmPriv->shortArrayCache.investment ); TRY_ALLOC_IF_NULL( pmPriv->shortArrayCache.profit ); TRY_ALLOC_IF_NULL( pmPriv->longArrayCache.investment ); TRY_ALLOC_IF_NULL( pmPriv->longArrayCache.profit ); #undef TRY_ALLOC_IF_NULL /* Reset to zero all the timeseries. */ memset( pmPriv->shortArrayCache.investment, 0, timeSerieSize ); memset( pmPriv->shortArrayCache.profit, 0, timeSerieSize ); memset( pmPriv->longArrayCache.investment, 0, timeSerieSize ); memset( pmPriv->longArrayCache.profit, 0, timeSerieSize ); /* Iterate through all the TA_TradeLog */ do { if( !(tradeLogPriv->flags & TA_PMARRAYCACHE_CALCULATED) ) processCache( pmPriv, tradeLogPriv ); tradeLogPriv = TA_ListAccessNext( tradeLogList ); } while( tradeLogPriv ); } pmPriv->flags |= TA_PMARRAYCACHE_CALCULATED; } switch( arrayId ) { case TA_PM_ARRAY_EQUITY: if( !(pmPriv->flags & TA_EQUITY_CALCULATED) ) { /* Allocate the daily equity. * Keep it cached in "pmPriv->equity". */ retCode = processDailyEquityArray(pmPriv,grp); if( retCode != TA_SUCCESS ) return retCode; pmPriv->flags |= TA_EQUITY_CALCULATED; } /* If requested is not daily, translate to the * new period. */ if( period == TA_DAILY ) { finalTimestamp = pmPriv->arrayTimestamp; finalData = pmPriv->equity; finalNbBars = pmPriv->nbDailyBars; } else { retCode = equityPeriodTransform( pmPriv, period, &finalNbBars, &finalTimestamp, &finalData ); if( retCode != TA_SUCCESS ) return retCode; } break; /*case TA_PM_ARRAY_RETURNS: break;*/ default: return TA_BAD_PARAM; } TA_ASSERT_RET( pmPriv->arrayTimestamp != NULL, TA_INTERNAL_ERROR(122) ); TA_ASSERT_RET( pmPriv->equity != NULL, TA_INTERNAL_ERROR(123) ); TA_ASSERT_RET( finalData != NULL, TA_INTERNAL_ERROR(124) ); TA_ASSERT_RET( finalTimestamp != NULL, TA_INTERNAL_ERROR(125) ); /* At last, allocate and fill up the TA_PMArray. */ newPMArray = TA_Malloc( sizeof( TA_PMArray ) ); if( !newPMArray ) return TA_ALLOC_ERR; newPMArray->arrayId = arrayId; newPMArray->grp = grp; newPMArray->period = period; newPMArray->data = finalData; newPMArray->timestamp = finalTimestamp; newPMArray->nbData = finalNbBars; newPMArray->hiddenData = pm; *allocatedArray = newPMArray; return TA_SUCCESS; }
/* Build the investment/profit time series. * * Note: A trade is a TA_DataLog with the quantity > 0 */ static TA_RetCode processCache( TA_PMPriv *pmPriv, TA_TradeLogPriv *tradeLog ) { TA_AllocatorForDataLog *allocator; TA_DataLogBlock *block; TA_List *list; TA_DataLog *invalidDataLog, *curDataLog; int i, idx; TA_Timestamp *exitTimestamp; /* Temporary values for calculation. */ register TA_Real tempReal1, tempReal2; register int tempInt1; /* Simply iterate through all the * TA_DataLogBlock and update the cached. */ allocator = &tradeLog->allocator; if( allocator ) { list = &allocator->listOfDataLogBlock; block = TA_ListAccessHead( list ); while( block ) { /* Process each blocks. */ invalidDataLog = allocator->nextAvailableTrade; curDataLog = block->array; for( i=0; i < TA_TRADE_BLOCK_SIZE; i++ ) { if( curDataLog == invalidDataLog ) { break; } else { /* Process each TA_DataLog being * a trade (not an entry) * An entry have a negative 'quantity'. */ tempInt1 = curDataLog->u.trade.quantity; if( tempInt1 > 0 ) { /* This is a trade. Consider only the trades that are * closed within the TA_PM start/end period. */ exitTimestamp = &curDataLog->u.trade.exitTimestamp; if( findTimestampIndex( pmPriv, exitTimestamp, &idx ) ) { tempReal1 = curDataLog->u.trade.entryPrice; /* Positive = long, negative = short */ tempReal2 = curDataLog->u.trade.profit; /* Positive = winning, negative = losing */ if( tempReal1 > 0.0 ) { /* This is a long trade. */ pmPriv->longArrayCache.investment[idx] += tempReal1; pmPriv->longArrayCache.profit[idx] += tempReal2; } else { /* This is a short trade. */ pmPriv->shortArrayCache.investment[idx] -= tempReal1; pmPriv->shortArrayCache.profit[idx] += tempReal2; } } } } curDataLog++; } block = TA_ListAccessNext( list ); } } return TA_SUCCESS; }
TA_RetCode TA_FileIndexMoveToNextToken( TA_FileIndexPriv *data ) { TA_PROLOG; TA_Libc *libHandle; if( !data ) return TA_UNKNOWN_ERR; libHandle = data->libHandle; TA_TRACE_BEGIN( libHandle, TA_FileIndexMoveToNextToken ); if( data->curToken == NULL ) { /* First time this function get called for this 'data' */ data->curTokenDepth = 0; data->curDirectionForward = 1; data->curToken = (TA_TokenInfo *)TA_ListAccessHead( data->listLocationToken ); data->prevToken = NULL; data->nextToken = (TA_TokenInfo *)TA_ListAccessNext( data->listLocationToken ); if( !data->curToken || !data->nextToken ) { TA_FATAL( data->libHandle, NULL, data->curToken, data->nextToken ); } } else if( data->nextToken == NULL ) { /* Can't go further, simply return. */ TA_TRACE_RETURN( TA_SUCCESS ); } else { if( data->curDirectionForward == 0 ) { if( data->prevToken != NULL ) TA_ListAccessNext( data->listLocationToken ); else TA_ListAccessHead( data->listLocationToken ); TA_ListAccessNext( data->listLocationToken ); data->curDirectionForward = 1; } data->prevToken = data->curToken; data->curToken = data->nextToken; data->nextToken = (TA_TokenInfo *)TA_ListAccessNext( data->listLocationToken ); /* Parano test: Should never happen since the code assume that * the 'listLocationToken' is always terminated by TA_END. */ if( !data->curToken ) { TA_FATAL( data->libHandle, NULL, data->curToken->id, data->curTokenDepth ); } } data->curTokenDepth++; TA_TRACE_RETURN( TA_SUCCESS ); }