Modules d'extension Lua
Contents
- 1 Que sont les modules d'extension Lua ?
- 2 Comment commencer ?
- 2.1 Préparation de l'environnement
- 2.2 Création du projet
- 2.3 Fichiers d'en-tête
- 2.4 Écriture du modèle de la fonction d'extension
- 2.5 Création de la description du module
- 2.6 Création de la fonction d'enregistrement du module
- 2.7 Préparation de l'indicateur test
- 2.8 Implémentation de la fonction d'extension
- 2.9 Déploiement et test
- 3 Problèmes connus
- 4 Cet article dans d'autres langues
Que sont les modules d'extension Lua ?
Le module d'extension Lua est une bibliothèque de liens dynamiques qui contient un ensemble de fonctions écrites en C ou en C++ pouvant être utilisées dans le code Lua.
Pour comprendre cet article, il faut connaître les MV Lua et le développement avec les MV Lua. Vous pouvez obtenir des informations sur les VM Lua dans la documentation sur Lua, Section 3. The Application Program Interface.
Notez que le module d'extension Lua peut être créé uniquement en C ou en C++. Cet article part du principe que Microsoft Visual Studio est utilisé mais n'importe quel compilateur de C++ Win 32 peut être employé.
Comment commencer ?
Préparation de l'environnement
Il vous suffit de télécharger et d'installer la dernière version d'Indicore Integration SDK. La version la plus récente est toujours disponible ici : IndicoreDev.exe
Création du projet
- Créez un nouveau projet C++/Win32.
- Choisissez un type d'application DLL et créez un projet vide.
- Dans les options de l'éditeur de liens, à la section « Input », ajoutez
C:\Gehtsoft\IndicoreDev\lib\lua5.1.lib
etC:\Gehtsoft\IndicoreDev\lib\indicore2.lib
à la propriété Additional Dependencies. - Le nom du fichier de sortie doit être identique au nom de la table d'extension que vous allez utiliser. Dans notre cas, nous allons appliquer la fonction de la valeur géométrique moyenne pour Lua. Nous appellerons donc notre module de sortie
geomean.dll
.
Fichiers d'en-tête
Ajoutez les en-têtes suivants à votre fichier source :
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <tchar.h>
#include <string>
#include <vector>
#include <map>
#include <list>
#define INDICORE2_EXTKIT
#include "C:/Gehtsoft/IndicoreDev/include/indicore2.h"
Écriture du modèle de la fonction d'extension
Ensuite, vous devez créer un modèle de fonction d'extension. Toutes les fonctions d'extension sont faites exactement sur le même modèle. Vous pouvez utiliser n'importe quel nom mais il est préférable de donner un nom semblable à celui que vous souhaitez utiliser.
/** geomean.calc(stream, from, to).
stream le flux de ticks
from la première période de l'intervalle du calcul
to la dernière période de l'intervalle du calcul
La fonction renvoie la valeur moyenne géométrique (un nombre)
*/
int geomean_calc(lua_State *L)
{
double ret = 0;
//À définir : calcul de la valeur moyenne dans une variable ret
lua_pushnumber(L, ret);
return 1;
}
Création de la description du module
Maintenant, il faut créer une description pour la table du module d'extension. La table du module d'extension est un ensemble de structures luaL_reg
. Chaque élément du tableau correspond à une fonction et définit le nom de la fonction tel qu'il est affiché dans Lua. Le dernier élément doit contenir uniquement des zéros.
static const struct luaL_reg geomean_lib [] =
{
{"calc", geomean_calc},
{NULL, NULL}
};
Création de la fonction d'enregistrement du module
Ensuite, il faut créer une fonction exportable qui sera appelée lorsque Lua devra charger notre module d'extension. Le nom de cette fonction doit être créé selon le modèle suivant : luaopen_name
où name correspond au nom du module d'extension (geomean dans notre exemple).
extern "C" int __declspec(dllexport) luaopen_extension (lua_State *L)
{
luaL_register(L, "geomean", geomean_lib);
lua_getglobal(L, "geomean");
if (lua_isnil(L, -1) || !lua_istable(L, -1)) {
luaL_error(L, "the geomean library is not registered");
}
return 1;
}
Préparation de l'indicateur test
L'indicateur est très semblable à la moyenne mobile simple (MVA.lua). Seul un paramètre entier N (nombre de périodes) est nécessaire. Il peut être appliqué sur les données de ticks et calculé à partir de la Ne barre. Créons le squelette de l'indicateur. Pour plus d'informations sur l'écriture des indicateurs, consultez Indicore SDK, notamment les instructions détaillées sur la création d'un indicateur simple.
function Init()
indicator:name("Test indicator");
indicator:description("");
indicator:requiredSource(core.Tick);
indicator:type(core.Indicator);
indicator.parameters:addGroup("Calculation");
indicator.parameters:addInteger("N", "Number of periods", "", 10, 2, 1000);
indicator.parameters:addGroup("Style");
indicator.parameters:addColor("C", "Line Color", "", core.rgb(0, 0, 255));
indicator.parameters:addInteger("W", "Line Width", "", 1, 1, 5);
indicator.parameters:addInteger("S", "Line Style", "", core.LINE_SOLID);
indicator.parameters:setFlag("S", core.FLAG_LEVEL_STYLE);
end
local n;
local first = nil;
local source = nil;
local out = nil;
require("geomean");
function Prepare(onlyName)
local name;
name = profile:id() .. "(" .. instance.source:name() .. ")";
instance:name(name);
if onlyName then
return ;
end
source = instance.source;
n = instance.parameters.N;
first = source:first() + n - 1;
out = instance:addStream("GEOMEAN", core.Line, name, "GEOMEAN", instance.parameters.C, first);
out:setWidth(instance.parameters.W);
out:setStyle(instance.parameters.S);
end
function Update(period, mode)
if period >= first then
out[period] = geomean.calc(source, period - n + 1, period);
end
end
Faites attention aux lignes surlignées. La première oblige Lua à charger votre extension. La seconde est un appel de notre fonction.
Implémentation de la fonction d'extension
Nous devons maintenant implanter la fonction d'extension. Tout d'abord, il faut récupérer les paramètres de la pile. Comme vous pouvez le voir dans le code, le premier paramètre est un flux. Nous devons donc convertir la table Lua en interface IIndicatorTickSource
. C'est pour cela qu'Indicore Integration SDK est nécessaire pour ce projet. Sa classe IndicoreAccessors
a été conçue pour cela.
Il vous faut donc obtenir les paramètres de la pile et vérifier les types.
/** geomean.calc(stream, from, to).
stream le flux de ticks
from la première période de l'intervalle du calcul
to la dernière période de l'intervalle du calcul
La fonction renvoie la valeur moyenne géométrique (un nombre)
*/
int geomean_calc(lua_State *L)
{
double ret = 0;
IIndicatorTickSource *src = IndicoreAccessors::getIIndicatorTickSource(L, 1);
if (src == 0)
return luaL_error(L, "le premier paramètre doit être la source.");
if (lua_isnumber(L, 2) == 0)
return luaL_error(L, "le deuxième paramètre doit être un nombre");
int from = (int)lua_tonumber(L, 2);
if (lua_isnumber(L, 3) == 0)
return luaL_error(L, "le troisième paramètre doit être un nombre");
int to = (int)lua_tonumber(L, 3);
lua_pushnumber(L, ret);
return 1;
}
Maintenant, vérifiez les paramètres « from » et « to ». Les deux valeurs doivent se trouver dans la fourchette du cours des données sources et la valeur « to » doit se trouver après la valeur « from ».
/** geomean.calc(stream, from, to).
stream le flux de ticks
from la première période de l'intervalle du calcul
to la dernière période de l'intervalle du calcul
La fonction renvoie la valeur moyenne géométrique (un nombre)
*/
int geomean_calc(lua_State *L)
{
double ret = 0;
IIndicatorTickSource *src = IndicoreAccessors::getIIndicatorTickSource(L, 1);
if (src == 0)
return luaL_error(L, "le premier paramètre doit être la source.");
if (lua_isnumber(L, 2) == 0)
return luaL_error(L, "le deuxième paramètre doit être un nombre");
int from = (int)lua_tonumber(L, 2);
if (lua_isnumber(L, 3) == 0)
return luaL_error(L, "le troisième paramètre doit être un nombre");
int to = (int)lua_tonumber(L, 3);
if (from < src->getFirstPeriod() || from >= src->size() ||
to < src->getFirstPeriod() || to >= src->size())
return luaL_error(L, "l'indice de période n'est pas dans l'intervalle");
if (to <= from)
return luaL_error(L, "le troisième paramètre doit être supérieur au deuxième");
lua_pushnumber(L, ret);
return 1;
}
Maintenant, appliquez le calcul de la moyenne géométrique. La formule pour calculer la moyenne géométrique de n valeurs est la suivante :
<math>\operatorname{geomean}(price, n)_i = \sqrt[n]{\prod_{j=i-n+1}^i price_j} = ({\prod_{j=i-n+1}^i price_j})^{\frac{1}{n}}</math>
/** geomean.calc(stream, from, to).
stream le flux de ticks
from la première période de l'intervalle du calcul
to la dernière période de l'intervalle du calcul
La fonction renvoie la valeur moyenne géométrique (un nombre)
*/
int geomean_calc(lua_State *L)
{
double ret = 0;
IIndicatorTickSource *src = IndicoreAccessors::getIIndicatorTickSource(L, 1);
if (src == 0)
return luaL_error(L, "le premier paramètre doit être la source.");
if (lua_isnumber(L, 2) == 0)
return luaL_error(L, "le deuxième paramètre doit être un nombre");
int from = (int)lua_tonumber(L, 2);
if (lua_isnumber(L, 3) == 0)
return luaL_error(L, "le troisième paramètre doit être un nombre");
int to = (int)lua_tonumber(L, 3);
if (from < src->getFirstPeriod() || from >= src->size() ||
to < src->getFirstPeriod() || to >= src->size())
return luaL_error(L, "l'indice de période n'est pas dans l'intervalle");
if (to <= from)
return luaL_error(L, "le troisième paramètre doit être supérieur au deuxième");
double n = (double)(to - from + 1);
double prod = 1;
for (int i = from; i <= to; i++)
prod *= src->getTick(i);
ret = pow(prod, 1.0 / n);
lua_pushnumber(L, ret);
return 1;
}
Voilà, l'extension est créée. Il suffit de la compiler.
Déploiement et test
- Placez
geomean.dll
compilé dans le dossier de l'application (par exemple Marketscope ou Indicator Debugger) où se trouvelua5.1.dll
(généralement, il s'agit du dossier où l'application est installée) ;
- Installez l'indicateur ;
- Exécutez l'indicateur.
Problèmes connus
Exceptions C++
La version utilisée de LuaJIT ne prend pas en charge les exceptions C++ pour le code Microsoft Visual C++/Win 32. Vous ne devez donc pas utiliser un droit try/catch dans la fonction appelée de Lua ou rappeler Lua d'un bloc try/catch.
Vous pouvez également consulter les liens suivants pour en savoir plus sur ce problème : documentation sur LuaJIT, fil de discussion en anglais.
Cet article dans d'autres langues
Language: | English • español • français • русский • 中文 • 中文(繁體) |
---|