📜 ⬆️ ⬇️

Writing a DLL for Metastock from scratch. Part 1

DLL  Metastock
Metastock is probably the most famous program for technical analysis of the market. This program can include external DLLs written by users to create their own trading strategies using the full power of traditional programming languages ​​such as C or Pascal.
Searching on the Internet, I was surprised to find a complete lack of information on this topic. The only article that turned out to be worthy of attention: “What is the Metastock Developer's Kit?” ( Mdk ), which describes a short example in Delphi, where you can download MDK.
In this article, I will try to fill this gap and describe the process of creating the external functions library Metastock (MSX DLL) in steps in C / C ++. All examples were written in Visual Studio 2010 ( part 2 , part 3 ).

Some theory


The functions that are implemented in the MSX DLL behave exactly like the standard Metastock built-in functions. All MSX DLL functions return an array of data. Each external function has a unique name.

ExtFml ("DLL Name.Function Name", arg1, ..., argn) , where

arg1 ... argn are function arguments.
')
Each argument can be one of four types:
• Datasets (for example, Open, High, Low, Close, etc., or the results of another function)
• Numeric constants (for example, 5, -5, 20.55, etc.)
• String constants (for example, “Hello Woodpecker”, etc.)
• Individual sets (for example, Simple, Triangular, etc.)
We will look at how to define and use them later, with specific examples.

The functions defined in the MSX DLL fall into two categories:
• Initialization Functions
• Calculation functions (or external functions).
Initialization functions are called by MetaStock during startup to determine which external functions are available and which arguments they require. Calculation functions are available for MetaStock users. All functions refer to the data structures defined in the MSXStruc.h file. This file is required to compile our DLL.
Before writing your own functions, you need to register several service MSX functions (initialization functions) in order for MetoStok to communicate with your DLL. There are four of them:
• MSXInfo is a required function. It is always called during initialization and checks whether our DLL is an MSX DLL and returns basic information about it (authorship, number of our functions, version).
• MSXNthFunction is a required function. It is called once for each function of the specified MSXInfo and numbers, starting with zero, our functions. Here the names of functions (case sensitive), their descriptor and the number of arguments for each are written.
• MSXNthArg - this function is required only if our functions have arguments. Called during initialization for each argument of our functions.
• MSXNthCustomString - this function is required only if our functions have custom arguments.

To begin the theory is enough, let's start writing the first DLL. This example will show how to display an array of prices, date and time in our indicator.

Write the code


We open VS 2010. First of all we will create the empty project.
File-> New-> Project-> Other Languages-> Visual C ++ -> Win32-> Win32 Console Application.
Set the name of our library (let UsePrice) and click OK. The Win32 Application Wizard opens.
Next-> and tick the DLL and the Empty project, click Finish. An empty project has been created. Add three files UsePrice.cpp, UsePrice.def, MSXStruc.h into it . Right-click on the project Add-> New Item ..., select the file with the appropriate extension and give it the corresponding name. Click Add.
In the file UsePrice.cpp we write our code.

/ *
- Step 1 - Headers
Comments
The #include directive instructs the compiler to read another source file -
in addition to the file in which this directive itself is located.
The source file name must be enclosed in double quotes or in angle brackets.

Headers for C Library Functions

* /
#include <string.h> #include <stdlib.h> #include <math.h> #include <float.h> #include <tchar.h> //   -  MSX Data Structures (.  MSXStruc.h). #include "MSXStruc.h" 

/ *
- Step 2 - Export
Comments
The #define directive defines the identifier and the sequence of characters
which identifier will be replaced when it is found in the text
programs. The identifier is also called the macro name, and the process
substitution is called macro substitution. Standard directive
following:

#define macro_name sequence of characters

Note that there is no semicolon in this operator. Between
identifier and sequence of characters can be any number
spaces. The macro is completed only by switching to a new line.

For example, if you want to use TRUE for a value of 1, a FALSE for 0,
then you can declare the following two macros:

#define TRUE 1
#define FALSE 0

As a result, if the compiler finds TRUE or FALSE in the program text,
he will replace them with 1 and 0 respectively.
In our case, if the code contains DLL_EXPORT, then the macro is executed

extern "C" __declspec (dllexport)

To normally conjugate C ++ code from C, where name mangling is missing, entered
extern "C", which disables this mechanism for exported variable / function names.
extern "C" refers to the use of simple function signature generation
(in C language style) when receiving object files. In particular, this prohibits
the C ++ compiler to “decorate” the function name with additional
characters when exporting to a DLL.
The dllexport storage class attribute is a Microsoft-specific extension.
C and C ++ languages. It can be used to export functions, data and objects.
to the DLL.
__declspec (dllexport) declarator
This attribute is explicitly defined by the DLL interface for its client, which may be
executable file or other DLL. Function declaration as
dllexport allows you to do without a module definition file (DEF), at least
least regarding the specification of exported functions.

* /
 #define DLL_EXPORT extern "C" __declspec(dllexport) 

/ *
- Step 3 - Initialization Functions
Comments
Before you write your functions, you must register several service
MSX-functions, so that the Stock can communicate with our DLL.
In the first example, we will write one function without arguments, therefore
MSXNthArg and MSXNthCustomString will not be needed.

MSXInfo
Replace the <VS 2010 C ++ MSX DLL, Copyright Pretzel, 2014> field with yours
information on authorship, as well as set the number of functions = 1.
strncpy (strDest, strSource, count) - copies the characters of one string to another.
• strDest is the destination string.
• strSource - the source line.
• count - the number of characters to copy.
strncpy requires the obligatory header <string.h>.
On strncpy, the VS C ++ compiler issues warnings, you can use
strncpy_s (checked - everything works, but I can not say how correct it is).

* /
 DLL_EXPORT BOOL __stdcall MSXInfo (MSXDLLDef *a_psDLLDef) { strncpy (a_psDLLDef->szCopyright, "VS 2010 C++ MSX DLL, Copyright (c) Pretzel, 2014", sizeof(a_psDLLDef->szCopyright)-1); a_psDLLDef->iNFuncs = 1; //  . a_psDLLDef->iVersion = MSX_VERSION; //  return MSX_SUCCESS; } 

/ *
Comments

MSXNthFunction
The value copied to a_sFuncDef-> szFunctionName must exactly match
our function, which we will export, is case sensitive.
Variable values:
• Name - the name of our function.
• Description - how it will be read in Metastock (in the 'Paste Functions' window).
• Arguments - the number of arguments of our function.
VS C ++ compiler issues warnings on strcpy, you can use strcpy_s
(checked - everything works, but I can’t say how correct it is).

* /
 DLL_EXPORT BOOL __stdcall MSXNthFunction (int a_iNthFunc, MSXFuncDef *a_psFuncDef) { BOOL l_bRtrn = MSX_SUCCESS; switch (a_iNthFunc) { case 0: strcpy (a_psFuncDef->szFunctionName, "Price"); strcpy (a_psFuncDef->szFunctionDescription, "FirstFunction"); a_psFuncDef->iNArguments = 0; //   break; default: l_bRtrn = MSX_ERROR; break; } return l_bRtrn; } 


/ *
- Step 4 - Our function

* /
 DLL_EXPORT BOOL __stdcall Price(const MSXDataRec *a_psBasic, const MSXDataInfoRecArgsArray *a_psArrayArgs, const MSXNumericArgsArray *a_psNumericArgs, const MSXStringArgsArray *a_psStringArgs, const MSXCustomArgsArray *a_psCustomArgs, MSXResultRec *a_psResult) { //     Close for (int i= a_psBasic ->sClose.iFirstValid; i<= a_psBasic ->sClose.iLastValid; i++) a_psResult->psResultArray->pfValue[ i ] = a_psBasic ->sClose.pfValue[ i ]; //    : // a_psResult->psResultArray->pfValue[ i ] = float (a_psBasic ->psDate[i].lDate); //  // a_psResult->psResultArray->pfValue[ i ] = float (a_psBasic ->psDate[i].lTime); //     . return MSX_SUCCESS; } 

Next, in the UsePrice.def file , enter the following code:

 LIBRARY UsePrice EXPORTS MSXInfo MSXNthFunction Price 

Fill in the MSXStruc.h code.
MSXStruc.h

 #ifndef MSX_Structures_h #define MSX_Structures_h /* Structures required for MetaStock External Function DLL interface */ #ifndef BOOL typedef int BOOL; #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif // -------------------------------------------------------------------------- // Return this DLL version constant // -------------------------------------------------------------------------- const int MSX_VERSION = 1; // -------------------------------------------------------------------------- // Maximum number of aguments // -------------------------------------------------------------------------- const int MSX_MAXARGS = 9; // -------------------------------------------------------------------------- // Maximum string size (does not include MSXString arguments passed in to // external functions). // -------------------------------------------------------------------------- const int MSX_MAXSTRING = 100; // -------------------------------------------------------------------------- // The following two BOOL return values are returned from MSX functions // -------------------------------------------------------------------------- const BOOL MSX_SUCCESS = FALSE; const BOOL MSX_ERROR = TRUE; // ---------------------------------------------------------------------------------------- // There are four potential argument types // ---------------------------------------------------------------------------------------- const int MSXDataArray = 0; const int MSXNumeric = 1; const int MSXString = 2; const int MSXCustom = 3; // ---------------------------------------------------------------------------------------- // The following structure is used by the exported function MSXInfo // ---------------------------------------------------------------------------------------- typedef struct { char szCopyright[MSX_MAXSTRING]; int iNFuncs; int iVersion; } MSXDLLDef; // ---------------------------------------------------------------------------------------- // The following structure is used by the exported function MSXNthFunction // ---------------------------------------------------------------------------------------- typedef struct { char szFunctionName[MSX_MAXSTRING]; char szFunctionDescription[MSX_MAXSTRING]; int iNArguments; } MSXFuncDef; // ---------------------------------------------------------------------------------------- // The following structure is used by the exported function MSXNthArg // ---------------------------------------------------------------------------------------- typedef struct { int iArgType; // argtype constants: // 0 DataArray // 1 Numeric // 2 String // 3 CustomType char szArgName[MSX_MAXSTRING]; int iNCustomStrings; } MSXFuncArgDef; // ---------------------------------------------------------------------------------------- // The following structure is used by the exported function MSXNthCustomString // ---------------------------------------------------------------------------------------- typedef struct { char szString[MSX_MAXSTRING]; int iID; } MSXFuncCustomString; // ---------------------------------------------------------------------------------------- // the following datastructures are passed into and out of the user-written external // calculation functions. // ---------------------------------------------------------------------------------------- typedef struct { long lDate; long lTime; } MSXDateTime; typedef struct { float *pfValue; int iFirstValid; int iLastValid; } MSXDataInfoRec; typedef struct { MSXDateTime *psDate; MSXDataInfoRec sOpen; MSXDataInfoRec sHigh; MSXDataInfoRec sLow; MSXDataInfoRec sClose; MSXDataInfoRec sVol; MSXDataInfoRec sOI; MSXDataInfoRec sInd; char *pszSecurityName; // Security Name char *pszSymbol; // Security Symbol char *pszSecurityPath; // Path where security is stored (may be in UNC format) char *pszOnlineSource; // Unused - reserved for future use... int iPeriod; // 'D'aily, 'W'eekly, 'M'onthly, 'Q'uarterly, 'I'ntraday int iInterval; // For period='I'ntraday only. 0=tick, other value = minutes compression. int iStartTime; // HHMM format. Undefined for non-intraday period. int iEndTime; // HHMM format. Undefined for non-intraday period. int iSymbolType; // Unused - reserved for future use } MSXDataRec; typedef struct { // possible for MSX_MAXARGS data arrays MSXDataInfoRec *psDataInfoRecs[MSX_MAXARGS]; // pointers to the data arrays int iNRecs; // number of arrays present (just a sanity check) } MSXDataInfoRecArgsArray; typedef struct { float fNumerics[MSX_MAXARGS]; // possible for MSX_MAXARGS numerics int iNRecs; // also a sanity check - func knows how many there should be. } MSXNumericArgsArray; typedef struct { char *pszStrings[MSX_MAXARGS]; // possible for MSX_MAXARGS strings int iNRecs; // ditto the above } MSXStringArgsArray; typedef struct { int iCustomIDs[MSX_MAXARGS]; // numeric ID associated with a custom arg int iNRecs; // ditto the above } MSXCustomArgsArray; typedef struct { MSXDataInfoRec *psResultArray; // Pointer to result array char szExtendedError[MSX_MAXSTRING]; // Extended Error string } MSXResultRec; #endif 


In Visual Studio, click Build -> Build Solution (F6) and get our DLL. Send it to the 'External Function DLLs' folder in Metastock and can be used. Our indicator will look like this:

ExtFml ("UsePrice.Price")

In the next part, we will look at our function, connect the arguments, add exceptions and output the data to the external environment.

Source: https://habr.com/ru/post/219133/


All Articles