Реклама

вторник, 16 ноября 2010 г.

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

Биржевой симулятор: класс TESBInstrument - симуляция покупки (продажи) акций. 

Исходники к уроку можно скачать здесь.
И так, продолжим писать биржевой симулятор. Сегодня мы реализуем новый класс: TESBInstrument, при помощи которого будем имитировать покупку и продажу акций. Пока, конечно, это будет тоже нечто вроде заготовки, как и в случае с TESBAccount, но в дальнейшем мы соединим эти классы и расширим функционал.
И так, объявление нового класса:
//класс симуляции остатка по биржевому инструменту
TESBInstrument=class(TESBBaseClass)
protected
     FPriceSource:TPASSPriceSource;
     FCount:integer;
     FName:string;
     FCurrentCandle:LongInt;
     FCanShort:boolean;
     procedure SetPriceSource(APriceSource:TPASSPriceSource);
public
     property PriceSource:TPASSPriceSource read FPriceSource write SetPriceSource;
     property Count:integer read FCount; // количество акций
     property Name:string read FName write FName;
     property CanShort:boolean read FCanShort write FCanShort; //можно ли шортит
     constructor Create;
     function CurrentPrice:double; //текущая цена по курсу открытия
     procedure Buy(ACount:integer); //купить
     procedure Sell(ACount:integer); //продать
     procedure Serialize(AStream:TStream); override;
     procedure Unserialize(AStream:TStream); override;
     function Next:boolean;
end;
Так как у нас используется класс TPASSPriceSource, то нам понадобится библиотека PASSBaseObj, ее можно скачать вот здесь Естественно, ее надо будет подключить к проекту, как вы уже научились на предыдущем уроке. Так же в модуле Base нужно добавить эту библиотеку в раздел uses*:

А теперь приступим к реализации класса:
// ************** TESBInstrument *****************
constructor TESBInstrument.Create;
begin
    inherited Create;
    FCanShort:=true;
    FCount:=0;
    FCurrentCandle:=0;
    FPriceSource:=nil;
end;

function TESBInstrument.CurrentPrice:double;
begin
    if FPriceSource<>nil then
    begin
       if FPriceSource.CurrentItemIndex<>FCurrentCandle then FPriceSource.CurrentItemIndex:=FCurrentCandle;
       Result:=FPriceSource.GetBarData.Open;
   end else raise Exception.Create('TESBInstrument.CurrentPrice -нет источника котировок');
end;

procedure TESBInstrument.Buy(ACount:integer);
begin
    FCount:=FCount+ACount;
end;

procedure TESBInstrument.Sell(ACount:integer);
begin
     if (ACount>FCount) and not(FCanShort) then raise Exception.Create('TESBInstrument.Sell - превышен лимит по инструменту');
     FCount:=FCount-ACount;
end;

procedure TESBInstrument.Serialize(AStream:TStream);
begin
    inherited Serialize(AStream);
    AStream.Write(FCanShort,sizeof(FCanShort));
    AStream.Write(FCount,sizeof(FCount));
    AStream.Write(FCurrentCandle,sizeof(FCurrentCandle));
    SerializeString(FName,AStream);
end;

procedure TESBInstrument.Unserialize(AStream:TStream);
begin
     inherited Unserialize(AStream);
    AStream.Read(FCanShort,sizeof(FCanShort));
    AStream.Read(FCount,sizeof(FCount));
    AStream.Read(FCurrentCandle,sizeof(FCurrentCandle));
    FName:=UnserializeString(AStream);
end;

procedure TESBInstrument.SetPriceSource(APriceSource:TPASSPriceSource);
begin
    FPriceSource:=APriceSource;
    FCurrentCandle:=APriceSource.CurrentItemIndex;
end;

function TESBInstrument.Next:boolean;
begin
     Result:=FPriceSource.NextItem;
     FCurrentCandle:=FCurrentCandle+1;
end;
И немного комментариев к коду. В прошлый раз, когда мы проектировали класс TESBAccount, свойство Cash (наличные), мы сделали только для чтения, предполагая, что его можно менять только методами. Аналогично сделаем и свойство Count, метод Buy увеличивает его, а Sell -уменьшает. Если по данной акции допустимы шорты CanShort то мы можем уменьшить Count в минус, иначе у нас при попытке продать больше чем есть будет вызвано исключение. Ну, и конечно же, для того, что бы знать цену акции, нам понадобиться источник котировок TPASSPriceSource. Данный класс  (TPASSPriceSource) позволяет загрузить котировки из текстового файла в формате, скаченного с Финама, а так же получить котировки на заданную свечу.
Теперь немного изменяем форму тестового примера, созданного на прошлом уроке*:
Вносим некоторые изменения в объявление класса TfrmStockBot*:
а затем переписываем обработчики событий:
procedure TfrmStockBot.FormCreate(Sender: TObject);
begin
   Instrument:=TESBInstrument.Create;
   Instrument.CanShort:=false;
   PriceSource:=nil;
end;

procedure TfrmStockBot.FormDestroy(Sender: TObject);
begin
    FreeAndNil(Instrument);
    if PriceSource<>nil then FreeAndNil(PriceSource);
end;

procedure TfrmStockBot.ShowCount;
begin
   mmAccount.Lines.Add(FloatToStr(Instrument.Count));
end;

procedure TfrmStockBot.btnBuyClick(Sender: TObject);
begin
   Instrument.Buy(StrToInt(edCount.Text));
   ShowCount;
end;

procedure TfrmStockBot.btnSellClick(Sender: TObject);
begin
   Instrument.Sell(StrToInt(edCount.Text));
   ShowCount;
end;

procedure TfrmStockBot.itSaveClick(Sender: TObject);
var Stream:TFileStream;
begin
   if SaveDialog.Execute then
   begin
     Stream:=TFileStream.Create(SaveDialog.FileName,fmCreate);
     Instrument.Serialize(Stream);
     FreeAndNil(Stream);
   end;
end;


procedure TfrmStockBot.itOpenClick(Sender: TObject);
var Stream:TFileStream;
begin
   if OpenDialog.Execute then
   begin
       Stream:=TFileStream.Create(OpenDialog.FileName,fmOpenRead);
       FreeAndNil(Instrument);
       Instrument:=TESBInstrument.Create;
       Instrument.PriceSource:=PriceSource;
       Instrument.Unserialize(Stream);
       FreeAndNil(Stream);
       mmAccount.Lines.Clear;
       ShowCount;
       lbPrice.Caption:=FloatToStr(Instrument.CurrentPrice);
   end;
end;

procedure TfrmStockBot.itOpenPricesClick(Sender: TObject);
begin
     if OpenDialogPrices.Execute then
     begin
         if PriceSource<>nil then FreeAndNil(PriceSource);
         PriceSource:=TPASSPriceSource.Create('',false);
         PriceSource.LoadDataFromTextFile(OpenDialogPrices.FileName);
         PriceSource.First;
         Instrument.PriceSource:=PriceSource;
         lbPrice.Caption:=FloatToStr(Instrument.CurrentPrice);
     end;
end;


procedure TfrmStockBot.btnNextClick(Sender: TObject);
begin
    Instrument.Next;
    lbPrice.Caption:=FloatToStr(Instrument.CurrentPrice);
end;
Также готовый тестовый пример можно скачать здесь
Затем запускаем программу, закачиваем котировки и приступаем к тестированию:

Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями  в соответствии со ст. 1274 ГК РФ программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation, (a Micro Focus Company).