static int isGapAcceptable( TA_Timestamp *lastBarTimestampAdded, TA_Timestamp *lastBarTimestamp ) { TA_RetCode retCode; unsigned int deltaDay, i; /* Verify if that gap in the data is acceptable. This is not * a "perfect" algorithm, but the idea is to avoid obvious * failure. Small failure might get through, but the consequence * won't be worst than a long week-end gap. */ retCode = TA_TimestampDeltaDay( lastBarTimestampAdded, lastBarTimestamp, &deltaDay ); if( (retCode != TA_SUCCESS) || (deltaDay >= 7) ) { /* A gap of more than 7 days is an error for sure. Or may be the symbol * is not being traded anymore? Don't take a chance and return an error. */ return 0; } /* The gap should not be more than 3 weekdays (after removing special case) */ retCode = TA_TimestampDeltaWeekday( lastBarTimestampAdded, lastBarTimestamp, &deltaDay ); if( retCode != TA_SUCCESS ) { return 0; } /* Handle special cases */ /* Trading were suspended on many exchange on september 11 2001 to september 14 2001 */ for( i=11; i <= 14; i++ ) { if( TA_DateWithinRange( 2001,9,i, lastBarTimestampAdded, lastBarTimestamp ) ) --deltaDay; } /* Handling holidays would be better here, any volunteer to implement this? */ if( deltaDay > 3 ) return 0; return 1; /* The gap is acceptable. */ }
/**** 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; }
TA_RetCode TA_EstimateAllocInit( const TA_Timestamp *start, const TA_Timestamp *end, TA_Period period, unsigned int minimumSize, unsigned int maximumSize, TA_EstimateInfo *estimationInfo, unsigned int *nbElementToAllocate ) { unsigned int nbElement; #if 0 /* Force extrems value for testing. */ *nbElementToAllocate = 1; return TA_SUCCESS; #endif nbElement = minimumSize; /* Default */ if( start && end ) { switch( period ) { case TA_DAILY: TA_TimestampDeltaDay( start, end, &nbElement ); break; case TA_WEEKLY: TA_TimestampDeltaWeek( start, end, &nbElement ); break; case TA_MONTHLY: TA_TimestampDeltaMonth( start, end, &nbElement ); break; case TA_QUARTERLY: TA_TimestampDeltaQuarter( start, end, &nbElement ); break; case TA_YEARLY: TA_TimestampDeltaYear( start, end, &nbElement ); break; default: if( (period >= TA_1SEC) && (period <= TA_1HOUR) ) { /* Estimate the number of day */ if( (TA_GetDay (start) != TA_GetDay (end)) || (TA_GetMonth(start) != TA_GetMonth(end)) || (TA_GetYear (start) != TA_GetYear (end)) ) { /* Estimate assuming market is open for 8 hours per day * (it does not hurt to slightly under or over estimate) */ TA_TimestampDeltaDay( start, end, &nbElement ); nbElement *= (8*60*60); nbElement /= period; } else { nbElement = (8*60*60); nbElement /= period; } } break; } nbElement += 2; } /* Make the estimation fits within the max/min provided. */ estimationInfo->maximumSize = maximumSize; estimationInfo->minimumSize = minimumSize; if( nbElement > maximumSize ) nbElement = maximumSize; if( nbElement < minimumSize ) nbElement = minimumSize; *nbElementToAllocate = nbElement; return TA_SUCCESS; }