Lenta kotirovok

Some blogs or websites linked from this site may contain objectionable or uncensored content, www.mainru.com is not affiliated with these websites and makes no representations or warranties as to their content.

понедельник, 1 ноября 2010 г.

Пишем робота Урок №3

Реализуем класс для расчета индикатора 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"

Источник : http://easyprog.ru/index.php?option=com_content&task=view&id=61&Itemid=44

Хвала и уважение автору МегаБаксу.

Поделится

Котировки

Идея