Rank: Advanced Member
Groups: Registered, Registered Users, Subscribers Joined: 9/8/2004(UTC) Posts: 2,266
Was thanked: 1 time(s) in 1 post(s)
|
I don't think I can post the manual but here is a sample code for C++ of Moving Average Function
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include "MSXStruc.h"
// we don't want C++ name-mangling
#ifdef __cplusplus
extern "C" {
#endif
const int NMyFuncs = 3; // three functions
// arg constants for MyMov
const int NMyMovArgs = 3; // 3 arguments
const int NMyMovCustStrings = 8; // 8 custom strings for third arg
// arg constants for AddN
const int NAddNArgs = 2; // 2 arguments
// arg constants for SumArrays
const int NSumArraysNArgs = 1; // 1 argument
BOOL __stdcall MSXInfo (MSXDLLDef *a_psDLLDef)
{
// copy in your copyright information...
strncpy (a_psDLLDef->szCopyright, "C Sample MSX DLL, Copyright (c) Equis International, 2000",
sizeof(a_psDLLDef->szCopyright)-1);
// set the number of functions we are exporting
// (don't forget to add them to your .def file)
a_psDLLDef->iNFuncs = NMyFuncs;
a_psDLLDef->iVersion = MSX_VERSION;
return MSX_SUCCESS;
}
BOOL __stdcall MSXNthFunction (int a_iNthFunc, MSXFuncDef *a_psFuncDef)
{
BOOL l_bRtrn = MSX_SUCCESS;
switch (a_iNthFunc) {
case 0:
strcpy (a_psFuncDef->szFunctionName, "MyMov");
strcpy (a_psFuncDef->szFunctionDescription, "My Moving Average");
a_psFuncDef->iNArguments = NMyMovArgs;
break;
case 1:
strcpy (a_psFuncDef->szFunctionName, "AddN");
strcpy (a_psFuncDef->szFunctionDescription, "Add 'n' to Any DataArray");
a_psFuncDef->iNArguments = NAddNArgs;
break;
case 2:
strcpy (a_psFuncDef->szFunctionName, "SumArrays");
strcpy (a_psFuncDef->szFunctionDescription, "Sum Initialed Price Arrays (OHLCVI)");
a_psFuncDef->iNArguments = NSumArraysNArgs;
break;
default:
l_bRtrn = MSX_ERROR;
break;
}
return l_bRtrn;
}
BOOL __stdcall MSXNthArg (int a_iNthFunc, int a_iNthArg,
MSXFuncArgDef *a_psFuncArgDef)
{
BOOL l_bRtrn = MSX_SUCCESS;
a_psFuncArgDef->iNCustomStrings = 0;
switch (a_iNthFunc) {
case 0:
switch (a_iNthArg) {
case 0:
a_psFuncArgDef->iArgType = MSXDataArray; // DataArray;
strcpy (a_psFuncArgDef->szArgName, "DataArray");
break;
case 1:
a_psFuncArgDef->iArgType = MSXNumeric; // Numeric
strcpy (a_psFuncArgDef->szArgName, "Period");
break;
case 2:
a_psFuncArgDef->iArgType = MSXCustom; // CustomType
a_psFuncArgDef->iNCustomStrings = NMyMovCustStrings;
strcpy (a_psFuncArgDef->szArgName, "Method");
break;
default:
l_bRtrn = MSX_ERROR;
break;
}
break;
case 1:
switch (a_iNthArg) {
case 0:
a_psFuncArgDef->iArgType = MSXDataArray;
strcpy (a_psFuncArgDef->szArgName, "DataArray");
break;
case 1:
a_psFuncArgDef->iArgType = MSXNumeric;
strcpy (a_psFuncArgDef->szArgName, "Increment");
break;
default:
l_bRtrn = MSX_ERROR;
break;
}
break;
case 2:
if (a_iNthArg == 0) {
a_psFuncArgDef->iArgType = MSXString;
strcpy(a_psFuncArgDef->szArgName, "DA Initials");
}
break;
default:
l_bRtrn = MSX_ERROR;
break;
}
return l_bRtrn;
}
BOOL __stdcall MSXNthCustomString (int a_iNthFunc, int a_iNthArg,
int a_iNthString, MSXFuncCustomString *a_psCustomString)
{
BOOL l_bRtrn = MSX_SUCCESS;
a_psCustomString->szString[0] = '\\0';
a_psCustomString->iID = -1;
typedef struct {
char *szString;
int iID;
} LocalStringElement;
LocalStringElement l_sTheStrings[] = {
{"Simple", 0}, {"S", 0},
{"Triangular", 1}, {"T", 1},
{"Weighted", 2}, {"W", 2},
{"Exponential", 3}, {"E", 3}
};
switch (a_iNthFunc) {
case 0:
switch (a_iNthArg) {
case 2:
if(a_iNthString >= 0 && a_iNthString < NMyMovCustStrings) {
strncpy (a_psCustomString->szString, l_sTheStrings[a_iNthString].szString,
sizeof(a_psCustomString->szString)-1);
a_psCustomString->iID = l_sTheStrings[a_iNthString].iID;
}
break;
default:
l_bRtrn = MSX_ERROR;
break;
}
break;
default:
l_bRtrn = MSX_ERROR;
break;
}
return l_bRtrn;
}
// ************************************************************************
// This local utility function is used to help ensure that no overflows
// or underflows will occur during calculations. The MSXTest program
// Stress Test function will call your DLL with a wide range of values,
// including positive and negative values of FLT_MAX and FLT_MIN.
// Perform all intermediate calculations using doubles and then force the
// results into the range of a float.
// Locally defined macros are used to avoid compiler definition differences.
// ************************************************************************
#define MSXMax(a,b) (((a) > (b)) ? (a) : (b))
#define MSXMin(a,b) (((a) < (b)) ? (a) : (b))
double ForceFloatRange (double a_lfDbl)
{
if (a_lfDbl > 0.0)
{
a_lfDbl = MSXMin (a_lfDbl, double(FLT_MAX)); // make sure pos number <= FLT_MAX
a_lfDbl = MSXMax (a_lfDbl, double(FLT_MIN)); // make sure pos number >= FLT_MIN
}
else
{
if (a_lfDbl < 0.0)
{
a_lfDbl = MSXMax (a_lfDbl, double(-FLT_MAX)); // make sure neg number >= -FLT_MAX
a_lfDbl = MSXMin (a_lfDbl, double(-FLT_MIN)); // make sure neg number <= -FLT_MIN
}
}
return a_lfDbl;
}
//------------------------------------------------------------------------
// This is an example of a local function used for calculations. This
// one calculates a moving average on the source data array, and puts
// the results in the result array. It differentiates its processing
// based on whether the moving average is to be weighted or not.
//------------------------------------------------------------------------
void MovingAverage (const MSXDataInfoRec *a_psSrc, MSXDataInfoRec *a_psRslt,
int a_iPeriod, BOOL a_bIsWeighted)
{
int l_iIndex = a_psSrc->iFirstValid;
int l_iMaxIndex = a_psSrc->iLastValid;
double l_lfSum = 0.0;
double l_lfDbl;
float l_fDivisor;
int i;
if (a_bIsWeighted)
l_fDivisor = float(a_iPeriod) * (float(a_iPeriod)+1.0f) / 2.0f; // sum of the digits formula
else
l_fDivisor = float(a_iPeriod);
l_fDivisor = float(ForceFloatRange (l_fDivisor));
if (l_fDivisor == 0.0)
l_fDivisor = 1.0f;
while ((l_iIndex + a_iPeriod - 1) <= l_iMaxIndex) {
l_lfSum = 0.0;
for (i=0; i<a_iPeriod; i++) {
if (a_bIsWeighted)
l_lfSum += a_psSrc->pfValue[l_iIndex+i] * (i + 1.0f);
else
l_lfSum += a_psSrc->pfValue[l_iIndex+i];
}
l_lfSum = ForceFloatRange(l_lfSum);
l_lfDbl = l_lfSum / l_fDivisor;
l_lfDbl = ForceFloatRange(l_lfDbl);
a_psRslt->pfValue[l_iIndex + a_iPeriod - 1] = float(l_lfDbl);
l_iIndex++;
}
a_psRslt->iFirstValid = a_psSrc->iFirstValid + a_iPeriod - 1;
a_psRslt->iLastValid = l_iMaxIndex;
}
//------------------------------------------------------------------------
// This is another example of a local function used for calculations. This
// one calculates an exponential moving average on the source data array,
// and puts the results in the result array. See 'The Technical Analysis
// Course' by Thomas A. Meyers for algorithm details.
//------------------------------------------------------------------------
void ExponentialMovingAverage (const MSXDataInfoRec *a_psSrc,
MSXDataInfoRec *a_psRslt,
int a_iPeriod)
{
int l_iIndex = a_psSrc->iFirstValid;
int l_iMaxIndex = a_psSrc->iLastValid;
double l_lfSum = 0.0;
double l_lfDivisor;
double l_lfExponent;
int i;
if (a_iPeriod > 0 && ((l_iIndex + a_iPeriod - 1) <= l_iMaxIndex)) {
l_lfExponent = ForceFloatRange(2.0 / (a_iPeriod+1));
l_lfDivisor = double(a_iPeriod);
// start with simple moving average;
for (i=0; i<a_iPeriod; i++)
l_lfSum += a_psSrc->pfValue[l_iIndex+i];
l_lfSum = ForceFloatRange(l_lfSum);
a_psRslt->pfValue[l_iIndex + a_iPeriod - 1] = float(ForceFloatRange(l_lfSum / l_lfDivisor));
l_iIndex += a_iPeriod;
while (l_iIndex <= l_iMaxIndex) {
a_psRslt->pfValue[l_iIndex] =
float(ForceFloatRange((a_psSrc->pfValue[l_iIndex] - a_psRslt->pfValue[l_iIndex-1]) *
l_lfExponent + a_psRslt->pfValue[l_iIndex-1]));
l_iIndex++;
}
a_psRslt->iFirstValid = a_psSrc->iFirstValid + a_iPeriod - 1;
a_psRslt->iLastValid = l_iMaxIndex;
}
else {
a_psRslt->iFirstValid = 0;
a_psRslt->iLastValid = -1;
}
}
//-------------------------------------------------------------------------------------------
// The following function demonstrates use of three argument types:
// MSXDataArray, MSXNumeric and MSXCustom.
// A MovingAverage is calculated on the input DataArray for input Periods.
// Three moving average methods are available, specified by the Custom ID.
//-------------------------------------------------------------------------------------------
BOOL __stdcall MyMov (const MSXDataRec *a_psDataRec,
const MSXDataInfoRecArgsArray *a_psDataInfoArgs,
const MSXNumericArgsArray *a_psNumericArgs,
const MSXStringArgsArray *a_psStringArgs,
const MSXCustomArgsArray *a_psCustomArgs,
MSXResultRec *a_psResultRec)
{
BOOL l_bRtrn = MSX_SUCCESS;
// We expect 3 arguments, 1 DataArray, 1 Numeric and 1 Custom, in that order
// The arguments will be found at:
// DataArray: a_psDataInfoArgs[0]
// Numeric : a_psNumericArgs->fNumerics[0];
// Custom : a_psCustomArgs->iCustomIDs[0];
const MSXDataInfoRec *l_psData;
int l_iPeriod;
int l_iMethod;
int l_iIndex;
int l_iMaxIndex;
if (a_psDataInfoArgs->iNRecs == 1 &&
a_psNumericArgs->iNRecs == 1 &&
a_psCustomArgs->iNRecs == 1) {
l_psData = a_psDataInfoArgs->psDataInfoRecs[0];
l_iPeriod = int(a_psNumericArgs->fNumerics[0]); // truncate any fractional period
l_iMethod = a_psCustomArgs->iCustomIDs[0];
l_iIndex = l_psData->iFirstValid;
l_iMaxIndex = l_psData->iLastValid;
if (l_iPeriod > 0 && (l_iIndex + l_iPeriod - 1) <= l_iMaxIndex &&
l_iIndex > 0 && l_iMaxIndex > 0 &&
l_iIndex >= a_psDataRec->sClose.iFirstValid &&
l_iMaxIndex <= a_psDataRec->sClose.iLastValid) {
switch (l_iMethod) {
case 0: // Simple
MovingAverage (l_psData, a_psResultRec->psResultArray, l_iPeriod, FALSE);
break;
case 1: // Triangular
{
MSXDataInfoRec l_sTmpRec;
l_sTmpRec.pfValue = new float[l_iMaxIndex+1];
if (l_sTmpRec.pfValue) {
l_iPeriod = (int)(((l_iPeriod+1.0)/2) + 0.5);
MovingAverage (l_psData, &l_sTmpRec, l_iPeriod, FALSE);
MovingAverage (&l_sTmpRec, a_psResultRec->psResultArray, l_iPeriod, FALSE);
delete l_sTmpRec.pfValue;
}
else {
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
}
break;
case 2: // Weighted
MovingAverage (l_psData, a_psResultRec->psResultArray, l_iPeriod, TRUE);
break;
case 3: // Exponential
ExponentialMovingAverage (l_psData, a_psResultRec->psResultArray, l_iPeriod);
break;
default:
{
// Somehow we got called with an invalid argument
strncpy (a_psResultRec->szExtendedError, "Undefined method argument",
sizeof(a_psResultRec->szExtendedError)-1);
l_bRtrn = MSX_ERROR; // report this as an error
}
break;
}
}
else {
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
}
else { // wrong number of arguments!
strncpy (a_psResultRec->szExtendedError, "Wrong number of arguments",
sizeof(a_psResultRec->szExtendedError)-1);
l_bRtrn = MSX_ERROR;
}
if (l_bRtrn != MSX_SUCCESS) { // only for serious errors...
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
return l_bRtrn;
}
//-------------------------------------------------------------------------------------------
// The following function will add a constant numeric value to any DataArray
//-------------------------------------------------------------------------------------------
BOOL __stdcall AddN (const MSXDataRec *a_psDataRec,
const MSXDataInfoRecArgsArray *a_psDataInfoArgs,
const MSXNumericArgsArray *a_psNumericArgs,
const MSXStringArgsArray *a_psStringArgs,
const MSXCustomArgsArray *a_psCustomArgs,
MSXResultRec *a_psResultRec)
{
BOOL l_bRtrn = MSX_SUCCESS;
int i;
// We expect 2 arguments, 1 DataArray, 1 Numeric
// The arguments will be found at:
// DataArray: theDataInfoArgs[0]
// Numeric : theNumericArgs->fNumerics[0];
if (a_psDataInfoArgs->iNRecs == 1 &&
a_psNumericArgs->iNRecs == 1) {
const MSXDataInfoRec *l_psData = a_psDataInfoArgs->psDataInfoRecs[0];
if (l_psData->iFirstValid > 0 && l_psData->iLastValid > 0 &&
l_psData->iFirstValid >= a_psDataRec->sClose.iFirstValid &&
l_psData->iLastValid <= a_psDataRec->sClose.iLastValid) {
float l_fAddOn = a_psNumericArgs->fNumerics[0];
if (l_psData) {
for (i=l_psData->iFirstValid; i<=l_psData->iLastValid; i++)
a_psResultRec->psResultArray->pfValue =
float (ForceFloatRange(l_psData->pfValue + l_fAddOn));
a_psResultRec->psResultArray->iFirstValid = l_psData->iFirstValid;
a_psResultRec->psResultArray->iLastValid = l_psData->iLastValid;
}
else {
strncpy (a_psResultRec->szExtendedError, "Data array argument missing",
sizeof(a_psResultRec->szExtendedError)-1);
l_bRtrn = MSX_ERROR;
}
}
else { // invalid incoming ranges
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
}
else { // wrong number of arguments!
strncpy (a_psResultRec->szExtendedError, "Wrong number of arguments",
sizeof(a_psResultRec->szExtendedError)-1);
l_bRtrn = MSX_ERROR;
}
if (l_bRtrn != MSX_SUCCESS) {
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
return l_bRtrn;
}
//-------------------------------------------------------------------------------------------
// The following function demonstrates the use of an MSXString argument.
// It parses the argument string for the initials identifying Open, High, Low, Close,
// Vol or Indicator, and sums their values into the result array.
// The result array FirstValid and LastValid are restricted to the common subset of
// all selected arrays.
//-------------------------------------------------------------------------------------------
BOOL __stdcall SumArrays (const MSXDataRec *a_psDataRec,
const MSXDataInfoRecArgsArray *a_psDataInfoArgs,
const MSXNumericArgsArray *a_psNumericArgs,
const MSXStringArgsArray *a_psStringArgs,
const MSXCustomArgsArray *a_psCustomArgs,
MSXResultRec *a_psResultRec)
{
BOOL l_bRtrn = MSX_SUCCESS;
int i;
int l_iFirstValid = a_psDataRec->sClose.iFirstValid;
int l_iLastValid = a_psDataRec->sClose.iLastValid;
const MSXDataInfoRec *l_psData;
// We expect 1 arguments, 1 String
// The argument will be found at:
// theStringArgs[0]
if (a_psStringArgs->iNRecs == 1) {
if (l_iFirstValid > 0 && l_iLastValid > 0) {
const char *l_pc = a_psStringArgs->pszStrings[0];
if (l_pc) {
// init result:
for (i=l_iFirstValid; i<=l_iLastValid; i++)
a_psResultRec->psResultArray->pfValue = 0.0f;
while (*l_pc) {
switch (*l_pc++) {
case 'O':
case 'o':
l_psData = &a_psDataRec->sOpen;
break;
case 'H':
case 'h':
l_psData = &a_psDataRec->sHigh;
break;
case 'L':
case 'l':
l_psData = &a_psDataRec->sLow;
break;
case 'C':
case 'c':
l_psData = &a_psDataRec->sClose;
break;
case 'V':
case 'v':
l_psData = &a_psDataRec->sVol;
break;
case 'I':
case 'i':
l_psData = &a_psDataRec->sInd;
break;
default:
l_psData = NULL;
break;
}
if (l_psData) {
l_iFirstValid = __max(l_iFirstValid, l_psData->iFirstValid);
l_iLastValid = __min(l_iLastValid, l_psData->iLastValid);
for (i=l_iFirstValid; i<=l_iLastValid; i++)
a_psResultRec->psResultArray->pfValue =
float(ForceFloatRange(a_psResultRec->psResultArray->pfValue +
l_psData->pfValue));
}
}
a_psResultRec->psResultArray->iFirstValid = l_iFirstValid;
a_psResultRec->psResultArray->iLastValid = l_iLastValid;
}
else {
// empty string!
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
}
else { // invalid first/last
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
}
else {// error - no argument passed!
strncpy (a_psResultRec->szExtendedError, "Wrong number of arguments",
sizeof(a_psResultRec->szExtendedError)-1);
l_bRtrn = MSX_ERROR;
}
if (l_bRtrn != MSX_SUCCESS) {
a_psResultRec->psResultArray->iFirstValid = 0;
a_psResultRec->psResultArray->iLastValid = -1;
}
return l_bRtrn;
}
#ifdef __cplusplus
}
#endif
Patrick
|