New Page 1
Реализуем класс для расчета индикатора momentum
(используем библиотеки и AfterConstruction, наследуемый от TObject)
Все статьи по данной теме.
Сегодня мы займемся реализацией биржевого робота. Точнее будем выполнять
задачу, поставленную на уроке
"Постановка задачи". Для начала нужно скачать с сайта
www.easyprog.ru нужные библиотеки (PASSBaseObj
и
PASSIndicators). Теперь создадим в Delphi
новый проект, скопируем туда закаченные библиотеки и добавим их в проект*:

В раздел uses добавим
подключенные к проекту модули PASSIndicators, PASSBaseObj:
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, PASSIndicators, PASSBaseObj;
|
Для для реализации расчета по заданной в уроке
"Математическая модель" формуле нам понадобиться создать новый класс
индикатора - TPASSMomentum. Объявим его:
TPASSMomentum=class(TPASSIndicator)
private
FDT:integer;
// длительность периода в интервалах
FPriceFieldType:string; //тип ценового поля (Open,
High, Low, Close);
procedure
UpdateParameters; virtual;
procedure
AfterConstruction; override;
protected
FParameters:TPASSParameters; // значения параметров
индикатора.
FPriceSource:TPASSPriceSource; // источник котировок.
public
constructor
Create(ADT:integer; APriceFieldType:string);
function
GetResultByFieldName(AFieldName:string):double; override;
function
GetResultByFieldNameAndIndex(AFieldName:string; Index:LongInt):double;
override;
function
GetResultByFieldNum(AFieldNum:integer):double; override;
function
GetResultByFieldNumAndIndex(AFieldNum:integer; Index:LongInt):double;
override;
procedure First;
override;
procedure Last;
override;
function
Next:boolean; override;
function
Prev:boolean; override;
function GetIndicatorName:string; override;
end;
|
Данный класс мы объявили как дочерний от класса
TPASSIndicator - это базовый класс индикатора. В модуле PASSIndicators есть еще
класса TPASSAverageIndicator, объявленный как дочерний от PASSIndicators,
он служит родительским классом для TPASSMASimple и TPASSADX, которые реализует
индикаторы Moving Average. ADX соответственно. Но
индикатор momentum, не является индикатором
скользящего среднего и для своего расчета не требует буфера расчета среднего
значений котировок, поэтому мы используем в качестве родительского класса
TPASSIndicator.
Класс TPASSIndicator имеет абстрактный метод UpdateParameters,
поэтому мы обязаны переопределить его в нашем классе. Это мы делаем в секции
private, так как данная процедура вызывается
только из методов класса индикатора и предназначена для переписывания значений
параметров из списка параметров в приватные поля класса. Почему именно так? Дело
в том, что при создании библиотек PASSBaseObj
и
PASSIndicators предполагалось, что они будут использованы для
серьезного приложения, похожего на Metatrader или
Metastock. А приватные поля используются только
главным образом для быстродействия, так как если обращаться через имя параметра,
то нужно будет искать его в списке, что замедлит работу программы. Это
замедление почувствуется, когда нужно будет делать много расчетов во вложенных
циклах.
В процедуре AfterConstruction, которая наследуется от
TObject, являющийся предком всех классов в
библиотеках, будет просто вызвана процедура UpdateParameters, поскольку в
TPASSIndicator это не сделано. Конечно, правильнее было бы переопределить
AfterConstruction непосредственно в TPASSIndicator, но рефреймингом кода мы
займемся потом, а сейчас наша задача поскорее создать индикатор и исследовать
его.
Так мы же определяем конструктор класса, что бы создать объект
класса на основании параметров. И переопределяем все абстрактные методы класса
TPASSIndicator . Кроме того, мы поля FParameters и FPriceSource из секции
private переносим в секцию
protected, просто объявив их как
protected в дочернем классе. Это нужно для того, что бы эти
поля стали доступные в дочернем классе но были по прежнему недоступные извне.
А теперь займемся реализацией. Начнем с UpdateParameters
procedure TPASSMomentum.UpdateParameters;
var i,cn:integer;
begin
cn:=FParameters.Count-1;
for i:=0 to cn do
begin
if UpperCase(FParameters[i].Name)='DT'
then FDT:=FParameters[i].Value else
begin
if UpperCase(FParameters[i].Name)='PRICEFIELDTYPE'
then FPriceFieldType:=FParameters[i].Value else
raise Exception.Create('No correct parameter name '+FParameters[i].Name);
end;
end;
end;
|
Здесь мы просто тупым перебором перебираем параметры и
устанавливаем соответствующие приватные поля. В списке есть параметр, которое не
соответствует ни одному приватному полю, то это нонсенс. Такого быть не должно.
Сразу вызываем исключение.
Далее реализуем метод
AfterConstruction и сам конструктор:
procedure TPASSMomentum.AfterConstruction;
begin
UpdateParameters;
end;
constructor TPASSMomentum.Create(ADT:integer; APriceFieldType:string);
begin
inherited Create;
FParameters.Add('DT',ADT);
FParameters.Add('PriceFieldType',APriceFieldType);
end; |
Методы
GetResultByFieldName и
GetResultByFieldNameAndIndex предназначены для получение значения индикатора
по имени функции (у индикаторов может быть несколько функций, например у
ADX есть функции DI+, DI- и
само ADX). У нашего же индикатора только одна функция
- Value. Реализуем получение значения по имени этой
функции:
function TPASSMomentum.GetResultByFieldName(AFieldName:string):double;
begin
if UpperCase(AFieldName)<>'VALUE' then Raise Exception.Create('TPASSMomentum.GetResultByFieldName: Неверное имя поля - '+AFieldName);
Result:=GetResultByFieldNum(0);
end;
function TPASSMomentum.GetResultByFieldNameAndIndex(AFieldName:string; Index:LongInt):double;
begin
if UpperCase(AFieldName)<>'VALUE' then Raise Exception.Create('TPASSMomentum.GetResultByFieldNameAndIndex: Неверное имя поля - '+AFieldName);
Result:=GetResultByFieldNumAndIndex(0,index);
end;
|
А теперь, наконец то добрались и до формулы:
function TPASSMomentum.GetResultByFieldNum(AFieldNum:integer):double;
var Pend,Pbeg:double;
begin
if FDT=0 then Raise Exception.Create('TPASSMomentum.GetResultByFieldNum: нулевое значение параметра DT');
if FPriceSource.CurrentItemIndex<=FDT then raise Exception.Create('TPASSMomentum.GetResultByFieldNum: ошибка выхода за диапазон');
if AFieldNum<>0 then Raise Exception.Create('TPASSMomentum.GetResultByFieldNum: Неверный номер поля - '+IntToStr(AFieldNum));
Pend:=FPriceSource.GetDataByFieldName(FPriceFieldType);
Pbeg:=FPriceSource.GetDataByFieldNameAndIndex('PriceFieldType',FPriceSource.CurrentItemIndex-FDT);
if Pbeg=0 then raise Exception.Create('TPASSMomentum.GetResultByFieldNum: Обнаружена нулевая котировка по полю '+
FPriceFieldType+' номер свечи '+IntToStr(FPriceSource.CurrentItemIndex-FDT)+' дата и время свечи '+
DateTimeToStr(FPriceSource.GetDataByFieldNameAndIndex('DateTime',FPriceSource.CurrentItemIndex-FDT)));
Result:=(Pend-Pbeg)/Pbeg*100/FDT;
end;
function TPASSMomentum.GetResultByFieldNumAndIndex(AFieldNum:integer; Index:LongInt):double;
var Pend,Pbeg:double;
begin
if AFieldNum<>0 then Raise Exception.Create('TPASSMomentum.GetResultByFieldNumAndIndex: Неверный номер поля - '+IntToStr(AFieldNum));
if FDT=0 then raise Exception.Create('TPASSMomentum.GetResultByFieldNumAndIndex: нулевое значение параметра DT');
if index<=FDT then Raise Exception.Create('TPASSMomentum.GetResultByFieldNumAndIndex: ошибка выхода за диапазон');
Pend:=FPriceSource.GetDataByFieldNameAndIndex(FPriceFieldType,Index);
Pbeg:=FPriceSource.GetDataByFieldNameAndIndex(FPriceFieldType,Index-FDT);
if Pbeg=0 then raise Exception.Create('TPASSMomentum.GetResultByFieldNumAndIndex: Обнаружена нулевая котировка по полю '+
FPriceFieldType+' номер свечи '+IntToStr(index-FDT)+' дата и время свечи '+
DateTimeToStr(FPriceSource.GetDataByFieldNameAndIndex('DateTime',index-FDT)));
Result:=(Pend-Pbeg)/Pbeg*100/FDT;
end;
|
Перед тем как произвести расчет по формуле мы проверяемы все возможные
ошибочные значения переменных и выдаем как можно более информативные исключения,
что бы потом можно было легко отлаживать программу.
Теперь реализуем оставшиеся методы:
procedure TPASSMomentum.Last;
begin
FPriceSource.Last;
end;
procedure TPASSMomentum.First;
begin
FPriceSource.CurrentItemIndex:=FDT+1;
end;
function TPASSMomentum.Next:boolean;
begin
Result:=FPriceSource.NextItem;
end;
function TPASSMomentum.Prev:boolean;
begin
Result:=FPriceSource.PrevItem;
end;
function TPASSMomentum.GetIndicatorName:string;
begin
Result:='Momentum';
end; |
Подведем итоги. На этом уроке мы разработали класс для расчета индикатора
momentum. На следующем уроке будем его тестировать.
Скриншоты, помеченные знаком * ,
являются цитатами и иллюстрациями в
соответствии со ст. 1274 ГК РФ программного
продукта "Delphi", авторское право на
который принадлежит "Borland Software Corporation".
|