Биржевой симулятор: класс 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; |
А теперь приступим к реализации класса:
// ************** 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).