unit MainForm; interface {.$define TICKDATA} uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.DateUtils, System.Generics.Collections, System.Rtti, System.Math, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base, FMX.ListView, FMX.Memo.Types, FMX.ScrollBox, FMX.Memo, FMX.Objects, Myc.Futures, Myc.Trade.Types, Myc.Trade.DataStream, Myc.Trade.Pipeline, Myc.Data.Series, Myc.Data.Pipeline, Myc.Signals, Myc.Mutable, Myc.Signals.FMX, Myc.TaskManager, Myc.Aura.Module, FMX.ListBox, FMX.Layouts, FMX.TreeView, FMX.TabControl, FMX.Menus, System.ImageList, FMX.ImgList, System.Actions, FMX.ActnList, DynamicFMXControl, Myc.FMX.Chart, Myc.Trade.Indicators, StrategyTest; type TForm1 = class(TForm) LogMemo: TMemo; MainPanel: TPanel; SymbolsComboBox: TComboBox; RandomBox: TCheckBox; LoadButton: TButton; RandomButton: TButton; ChartButton: TButton; StopButton: TButton; Splitter1: TSplitter; ActionList: TActionList; AddWorkspaceAction: TAction; WorkspacePanel: TPanel; TabControl: TTabControl; ToolBar: TToolBar; AddWorkspaceButton: TSpeedButton; ObjectsPanel: TPanel; ObjectsTabControl: TTabControl; ModulesTabItem: TTabItem; TreeView: TTreeView; TestButton: TSpeedButton; TestAction: TAction; TestPopup: TPopup; FlowLayout: TFlowLayout; StrategyButton: TSpeedButton; Strat2Button: TSpeedButton; Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure StopButtonClick(Sender: TObject); procedure TreeViewDblClick(Sender: TObject); procedure AddWorkspaceActionExecute(Sender: TObject); procedure Strat2ButtonClick(Sender: TObject); procedure TestActionExecute(Sender: TObject); procedure StrategyButtonClick(Sender: TObject); private FOnEvent: TNotifyEvent; { Private declarations } {$ifdef TICKDATA} FServer: IDataServer; {$else} FServer: IDataServer; {$endif} FSymbols: TFuture>; FTerminate: TEvent; FProcessDone: TState; FApplication: IAuraApplication; FModulesItem: TTreeViewItem; function SelectedSymbol: String; procedure ExecuteStrategy(const Symbol: String; Timeframe: TTimeframe; const Consumer: IConsumer>); public procedure NewWorkspace; function CurrLayout: T; procedure AlignControl(Control: TControl); function CreateStrategy2(Timeframe: TTimeframe): IConsumer>; published property OnEvent: TNotifyEvent read FOnEvent write FOnEvent; end; var Form1: TForm1; implementation uses Myc.Data.Records, TestModule; {$R *.fmx} procedure TForm1.NewWorkspace; var tab: TTabItem; begin var ws: IAuraWorkspace := TMycAuraWorkspace.Create('New workspace', tmTesting); FApplication.Workspaces.Insert(-1, ws); tab := TabControl.Add; tab.Text := ws.Caption; tab.Tag := NativeInt(ws); var scrollbox := TVertScrollBox.Create(Self); scrollbox.Parent := tab; scrollbox.Align := TAlignLayout.Client; tab.ProcessSignal(ws.Name.Changed, procedure begin tab.Text := ws.Caption; end); end; procedure TForm1.StopButtonClick(Sender: TObject); begin FTerminate.Notify; TaskManager.WaitFor(FProcessDone); var Layout := CurrLayout; if Layout <> nil then begin Layout.Content.DeleteChildren; Layout.Repaint; end; end; procedure TForm1.TreeViewDblClick(Sender: TObject); begin var sel := TreeView.Selected as TTreeViewItem; var parent := sel.ParentItem; if parent = nil then exit; if parent = FModulesItem then begin if (TabControl.ActiveTab <> nil) and (TabControl.ActiveTab.Tag <> 0) then begin var ws := IAuraWorkspace(TabControl.ActiveTab.Tag); var module := FApplication.Modules[sel.TagString]; if (ws <> nil) and (module <> nil) then module.SetupWorkspace(ws); end; end; end; procedure TForm1.FormCreate(Sender: TObject); begin FApplication := TMycAuraApplication.Create; FModulesItem := TTreeViewItem.Create(Self); FModulesItem.Text := 'Modules'; const modName = 'Test_Module'; FApplication.RegisterModule(modName, TTestModule.Create('Test-Module', 1)); TreeView.AddObject(FModulesItem); FModulesItem.ProcessSignal( FApplication.ModuleNames.Changed, procedure begin FModulesItem.BeginUpdate; try while FModulesItem.Count > 0 do FModulesItem[0].Free; var mods := FApplication.ModuleNames.Value; for var i := 0 to High(mods) do begin var ModItem := TTreeViewItem.Create(Self); ModItem.Text := mods[i]; ModItem.DragMode := TDragMode.dmAutomatic; ModItem.Text := 'Module-' + mods[i]; ModItem.TagString := mods[i]; FModulesItem.AddObject(ModItem); end; FModulesItem.ExpandAll; finally FModulesItem.EndUpdate; end; end ); FTerminate := TEvent.CreateEvent; // Create an instance of the TAuraTABFileServer. The server can be reused for multiple stream creations. [364] {$ifdef TICKDATA} FServer := TAuraTABFileServer.Create('\\COFFEE\TickData\Pepperstone'); {$else} FServer := TAuraM1FileServer.Create('\\COFFEE\TickData\Pepperstone'); {$endif} SymbolsComboBox.Enabled := false; ChartButton.Enabled := false; LoadButton.Enabled := false; FSymbols := FServer.EnumerateSymbols; SymbolsComboBox.ProcessSignal( FSymbols.Done.Signal, procedure begin SymbolsComboBox.BeginUpdate; try SymbolsComboBox.Items.Clear; SymbolsComboBox.Items.AddStrings(FSymbols.WaitFor); if SymbolsComboBox.Items.Count > 0 then begin if SymbolsComboBox.ItemIndex < 0 then SymbolsComboBox.ItemIndex := SymbolsComboBox.Items.IndexOf('GER40'); SymbolsComboBox.Enabled := true; ChartButton.Enabled := true; LoadButton.Enabled := true; end; finally SymbolsComboBox.EndUpdate; end; end ); NewWorkspace; end; procedure TForm1.FormDestroy(Sender: TObject); begin FSymbols.WaitFor; FTerminate.Notify; TaskManager.WaitFor(FProcessDone); end; procedure TForm1.TestActionExecute(Sender: TObject); begin TestPopup.IsOpen := TestAction.Checked; end; procedure TForm1.AddWorkspaceActionExecute(Sender: TObject); begin NewWorkspace; end; procedure TForm1.AlignControl(Control: TControl); begin var Layout := CurrLayout; if Layout = nil then exit; Control.Parent := Layout; Control.Width := Layout.Width; Control.Position.Y := Layout.ChildrenRect.Bottom + 1; Control.Anchors := [TAnchorKind.akLeft, TAnchorKind.akRight]; Control.Align := TAlignLayout.Top; end; function TForm1.CreateStrategy2(Timeframe: TTimeframe): IConsumer>; type TSignal = record Sig: Double; SL: Double; Entry: Double; pnl: Double; end; var panel: TMycChart.TPanel; begin var ticker := TConverter.CreateIdentity>; Result := ticker.Consumer; var OhlcPoint := ticker.Producer.Chain>(TTradeConverter.CreateOhlcAggregation(Timeframe)); var Ohlc := OhlcPoint.Field('Data'); var Closes := Ohlc.Field('Close'); var Hull := Closes.Chain(TIndicators.CreateHMA(250)).MakeParallel; var Sma := Closes.Chain(TIndicators.CreateSMA(200)).MakeParallel; var Lowest: Double := Double.MaxValue; var Highest: Double := Double.MinValue; var ATR := Ohlc.Chain(TIndicators.CreateATR(50)).MakeParallel; // next stage var curr: TSignal; curr.SL := Double.NaN; curr.Entry := Double.NaN; var lastHull, lastSma: Double; var conv := TConverter.Join([Ohlc.Field('Low'), Ohlc.Field('High'), Closes, ATR, Hull, Sma]); var Signal := TConverter, TSignal>.CreateConverter( function(const Values: TArray): TSignal begin var low := Values[0]; var high := Values[1]; var close := Values[2]; var atr := Values[3]; var hull := Values[4]; var sma := Values[5]; if low < Lowest then Lowest := low; if high > Highest then Highest := high; Result := curr; Result.Sig := 0; var pnl: double := NaN; if (hull < sma) and (lastHull >= lastSma) then begin if curr.Sig > 0 then pnl := close - curr.Entry; curr.Sig := -1; curr.SL := Highest; curr.Entry := close; Result := curr; end else if (hull > sma) and (lastHull <= lastSma) then begin if curr.Sig < 0 then pnl := curr.Entry - close; curr.Sig := 1; curr.SL := Lowest; curr.Entry := close; Result := curr; end; atr := 15 * atr; if curr.Sig > 0 then begin if close > curr.SL then begin if curr.SL < close - atr then curr.SL := close - atr; Result.SL := curr.SL; end; if low <= curr.SL then begin pnl := curr.SL - curr.Entry; curr.Sig := 0; Result.Sig := 0; curr.SL := NaN; end; end else if curr.Sig < 0 then begin if close < curr.SL then begin if curr.SL > close + atr then curr.SL := close + atr; Result.SL := curr.SL; end; if high >= curr.SL then begin pnl := curr.Entry - curr.SL; curr.Sig := 0; Result.Sig := 0; curr.SL := NaN; end; end; if Result.Sig <> 0 then begin Lowest := Double.MaxValue; Highest := Double.MinValue; Result.SL := Double.NaN; Result.Entry := Double.NaN; end; Result.pnl := pnl; lastHull := hull; lastSma := sma; end ); conv.Chain(Signal); var pnl := Signal.Producer.Field('pnl'); var FEquity: Double := 10000; var FInit: Boolean := false; var equity := TConverter.CreateAggregation( function(const Value: Double; const Broadcast: TBroadcastFunc): TState begin if not FInit then begin FInit := true; Broadcast(FEquity); end; if not IsNan(Value) then begin FEquity := FEquity + Value; Result := Broadcast(FEquity); end; end ); pnl.Chain(equity); var Layout := CurrLayout; if Layout = nil then exit; var Symbol := SelectedSymbol; if Symbol = '' then exit; var chart := TMycChart.Create(Self); AlignControl(chart); chart.Height := Layout.ChildrenRect.Width * 9 / 16; chart.Lookback.Value := 50000; chart.SetXAxisSeries(M15, OhlcPoint.Field('Time')); panel := chart.AddPanel; panel.AddOhlcSeries(Ohlc); panel.AddDoubleSeries(Hull, TAlphaColors.Cornflowerblue, 2); panel.AddDoubleSeries(Sma, TAlphaColors.Brown, 1.5); panel.AddDoubleSeries(Signal.Producer.Field('Entry'), TAlphaColors.Green, 1); panel.AddDoubleSeries(Signal.Producer.Field('SL'), TAlphaColors.Red, 2); var mean := TConverter, Double>.CreateConverter(TIndicators.CreateMean()); TConverter.Join([Hull, Sma]).Chain(mean); panel.AddDoubleSeries(mean.Producer, TAlphaColors.Blue, 5); var pnlChart := TMycChart.Create(Self); AlignControl(pnlChart); pnlChart.Height := Layout.ChildrenRect.Width * 9 / 24; pnlChart.Lookback.Value := 50000; pnlChart.SetXAxisCounter(equity.Producer); //////////// var EMAFactory := TEMA.Create; var Params := TDataRecord.Create(EMAFactory.Params); Params.SetValue('Period', 20); var indi := EMAFActory.CreateIndicator(Params); var EMAConv := TConverter.CreateConverter(indi); var equityEMA := equity .Producer .Chain(EMAFactory.Input.FieldAsRecord('Price')) .Chain(EMAConv) .Chain(EMAFactory.Output.FieldOfRecord('MA')); ////////////// panel := pnlChart.AddPanel; panel.AddDoubleSeries(equity.Producer, TAlphaColors.Blue, 3); panel.AddDoubleSeries(equityEMA, TAlphaColors.Gray, 2); ///// end; function TForm1.CurrLayout: T; begin if TabControl.ActiveTab = nil then exit(nil); var Res: T := nil; TabControl.ActiveTab.EnumControls( function(Control: TControl): TEnumControlsResult begin Result := TEnumControlsResult.Continue; if Control is T then begin Res := Control as T; Result := TEnumControlsResult.Stop; end; end ); Result := Res; end; procedure TForm1.ExecuteStrategy(const Symbol: String; Timeframe: TTimeframe; const Consumer: IConsumer>); begin var terminated := TFlag.CreateObserver(FTerminate.Signal).State; {$ifdef TICKDATA} var ticker := TConverter.CreateTicker>; var lastPrice := ticker.Chain>( function(const Tick: TDataPoint): TDataPoint begin Result.Time := Tick.Time; Result.Data := 0.5 * (Tick.Data.Ask + Tick.Data.Bid); end ); var OhlcPoint := lastPrice.Chain>(TConverter.CreateTickAggregation(Timeframe)); OhlcPoint.Producer.Link(Consumer); var Producer := TConverter>, TArray>>.CreateGeneric( function(const Values: TArray>): TArray> begin SetLength(Result, Length(Values)); for var i := 0 to High(Result) do begin Result[i].Time := Values[i].Time; Result[i].Data.Ask := Values[i].Data.Ask; Result[i].Data.Bid := Values[i].Data.Bid; end; end ); Producer.Producer.Link(ticker); FProcessDone := FProcessDone + FServer.ProcessData(Symbol, terminated, Producer); {$else} var ticker := TConverter.CreateTicker>; ticker.Producer.Chain(Consumer); FProcessDone := FProcessDone + FServer.ProcessData(Symbol, terminated, ticker.Consumer); {$endif} end; function TForm1.SelectedSymbol: String; begin Result := ''; if RandomBox.IsChecked then Result := FSymbols.WaitFor[Random(Length(FSymbols.WaitFor))] else if SymbolsComboBox.ItemIndex >= 0 then Result := FSymbols.WaitFor[SymbolsComboBox.ItemIndex]; end; procedure TForm1.StrategyButtonClick(Sender: TObject); begin var Layout := CurrLayout; if Layout = nil then exit; var Symbol := SelectedSymbol; if Symbol = '' then exit; var chart := TMycChart.Create(Self); AlignControl(chart); chart.Height := Layout.ChildrenRect.Width * 9 / 16; chart.Lookback.Value := 50000; ///// var timeframe := TTimeframe.H; var ticker := TConverter.CreateIdentity>; var OhlcPoint := ticker.Producer.Chain>(TTradeConverter.CreateOhlcAggregation(timeframe)); // var OhlcTicker := TConverter.CreateIdentity>; // var OhlcPoint := OhlcTicker.Sender; var Timestamps := OhlcPoint.Field('Time'); var Ohlc := OhlcPoint.Field('Data'); var Closes := Ohlc.Field('Close'); var Hull := Closes.Chain(TIndicators.CreateHMA(150)); var Sma := Closes.Chain(TIndicators.CreateSMA(50)); var Ema := Closes.Chain(TIndicators.CreateEMA(21)); var Boli := Closes.MakeParallel.Chain(TIndicators.CreateBollingerBands(20, 2.0)); var Rsi := Closes.Chain(TIndicators.CreateRSI(14)); var Macd := Closes.MakeParallel.Chain(TIndicators.CreateMACD(12, 26, 9)); var Stoch := Ohlc.Chain(TIndicators.CreateStochastic(14, 3)); chart.SetXAxisSeries(timeframe, Timestamps); var Panel := chart.AddPanel; Panel.AddOhlcSeries(Ohlc); Panel.AddDoubleSeries(Hull, TAlphaColors.Aliceblue); Panel.AddDoubleSeries(Sma, TAlphaColors.Yellow); Panel.AddDoubleSeries(Ema, TAlphaColors.Aqua); Panel.AddDoubleSeries(Boli.Field('UpperBand'), TAlphaColors.Gray); Panel.AddDoubleSeries(Boli.Field('MiddleBand'), TAlphaColors.Darkgray, 1.0); Panel.AddDoubleSeries(Boli.Field('LowerBand'), TAlphaColors.Gray); Panel := chart.AddPanel; Panel.AddDoubleSeries(Rsi, TAlphaColors.Fuchsia); Panel := chart.AddPanel; Panel.AddDoubleSeries(Macd.Field('MacdLine'), TAlphaColors.Orange); Panel.AddDoubleSeries(Macd.Field('SignalLine'), TAlphaColors.Dodgerblue); Panel.AddDoubleSeries(Macd.Field('Histogram'), TAlphaColors.Lightgreen); Panel := chart.AddPanel; Panel.AddDoubleSeries(Stoch.Field('K'), TAlphaColors.Green); Panel.AddDoubleSeries(Stoch.Field('D'), TAlphaColors.Red); ///// { var tickChart := TMycChart.Create(Self); tickChart.Height := Layout.ChildrenRect.Width * 9 / 16; AlignControl(tickChart); tickChart.Lookback.Value := 1000000; var TickTime := ticker.Field('Time'); var TickData := ticker.Field('Data'); var TickAsk := TickData.Field('Ask'); var TickBid := TickData.Field('Bid'); var TickSpread := TickData.Chain(function(const Tick: TAskBidItem): Double begin Result := Tick.Bid - Tick.Ask; end); tickChart.SetXAxisSeries(TTimeframe.S, TickTime.Sender); panel := tickChart.AddPanel; panel.AddDoubleSeries(TickAsk.Sender, TAlphaColors.Blue); panel.AddDoubleSeries(TickBid.Sender, TAlphaColors.Red); panel := tickChart.AddPanel; panel.AddDoubleSeries(TickSpread.Sender); } ///// ExecuteStrategy(Symbol, timeframe, ticker.Consumer); end; procedure TForm1.Strat2ButtonClick(Sender: TObject); begin var Symbol := SelectedSymbol; if Symbol = '' then exit; var timeframe := TTimeframe.M15; //ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe)); var tstStrat := StrategyTest.CreateStrategy1(timeframe); ExecuteStrategy(Symbol, timeframe, tstStrat.Consumer); var Layout := CurrLayout; if Layout = nil then exit; var pnlChart := TMycChart.Create(Self); AlignControl(pnlChart); pnlChart.Height := Layout.ChildrenRect.Width * 9 / 24; pnlChart.Lookback.Value := 50000; pnlChart.SetXAxisCounter(tstStrat.Producer); var panel := pnlChart.AddPanel; panel.AddDoubleSeries(tstStrat.Producer, TAlphaColors.Blue, 3); end; end.