Files
MycLib/AuraTrader/MainForm.pas
T
2025-07-13 14:20:30 +02:00

554 lines
17 KiB
ObjectPascal

unit MainForm;
interface
uses
System.SysUtils,
System.Types,
System.UITypes,
System.Classes,
System.Variants,
System.DateUtils,
System.Generics.Collections,
System.Rtti,
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.DataStream,
Myc.Trade.DataPoint,
Myc.Trade.DataProvider,
Myc.Signals,
Myc.Lazy,
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,
FirstStrategy,
Myc.FMX.Chart;
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;
procedure RandomButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure LoadButtonClick(Sender: TObject);
procedure ChartButtonClick(Sender: TObject);
procedure StopButtonClick(Sender: TObject);
procedure LayoutResized(Sender: TObject);
procedure TreeViewDblClick(Sender: TObject);
procedure AddWorkspaceActionExecute(Sender: TObject);
procedure TestActionExecute(Sender: TObject);
procedure StrategyButtonClick(Sender: TObject);
private
const
cnt = 20;
type
TRndItem = record
Stream: IDataStream<TAuraAskBidFileItem>;
Data: TMutable<TDataSeries<TAuraAskBidFileItem>>;
Labl: TLabel;
end;
private
FOnEvent: TNotifyEvent;
{ Private declarations }
FServer: IDataServer<TAuraAskBidFileItem>;
FSymbols: TFuture<TArray<String>>;
FRandom: TList<TRndItem>;
FTerminate: TEvent;
FProcessDone: TState;
FApplication: IAuraApplication;
FModulesItem: TTreeViewItem;
function SelectedSymbol: String;
function ExecuteStrategy(const Symbol: String; const Processor: IMycProcessor<TArray<TDataPoint<TAskBidItem>>>): TState;
public
procedure NewWorkspace;
function CurrLayout<T: TControl>: T;
procedure AlignControl(Control: TControl);
published
property OnEvent: TNotifyEvent read FOnEvent write FOnEvent;
end;
var
Form1: TForm1;
implementation
uses
TestModule;
{$R *.fmx}
procedure TForm1.NewWorkspace;
begin
var ws: IAuraWorkspace := TMycAuraWorkspace.Create('New workspace', tmTesting);
FApplication.Workspaces.Insert(-1, ws);
var 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;
end;
procedure TForm1.StrategyButtonClick(Sender: TObject);
begin
var Layout := CurrLayout<TVertScrollBox>;
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 OhlcPoint: IMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>> := TTicksToTimeframe.Create(M1);
var Timestamps: IMycConverter<TDataPoint<TOhlcItem>, TDateTime> :=
TMycGenericConverter<TDataPoint<TOhlcItem>, TDateTime>
.Create(function(const Ohlc: TDataPoint<TOhlcItem>): TDateTime begin Result := Ohlc.Time; end);
var Ohlc: IMycConverter<TDataPoint<TOhlcItem>, TOhlcItem> :=
TMycGenericConverter<TDataPoint<TOhlcItem>, TOhlcItem>
.Create(function(const Ohlc: TDataPoint<TOhlcItem>): TOhlcItem begin Result := Ohlc.Data; end);
OhlcPoint.Sender.Link(Ohlc);
var Closes: IMycConverter<TOhlcItem, Double> :=
TMycGenericConverter<TOhlcItem, Double>.Create(function(const Ohlc: TOhlcItem): Double begin Result := Ohlc.Close; end);
Ohlc.Sender.Link(Closes);
for var i := 0 to 3 do
begin
var Hull: IMycConverter<Double, Double> := THullMovingAverage.Create(50 + (500 * i));
Closes.Sender.Link(Hull);
var col: TAlphaColorRec;
col.R := 25 * i;
col.G := 255 - 25 * i;
col.B := 100 + 5 * i;
col.A := 255;
chart.AddDoubleSeries(Hull.Sender, col.Color);
end;
// var Hull: IMycConverter<Double, Double> := THullMovingAverage.Create(250);
//
// Closes.Sender.Link(Hull);
//
// chart.AddDoubleSeries(Hull.Sender);
OhlcPoint.Sender.Link(TimeStamps);
chart.SetXAxisSeries<TDateTime>(Timestamps.Sender);
chart.AddOhlcSeries(Ohlc.Sender);
var done := ExecuteStrategy(Symbol, OhlcPoint);
/////
FProcessDone := TState.All([FProcessDone, done]);
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;
FRandom := TList<TRndItem>.Create;
// Create an instance of the TAuraTABFileServer. The server can be reused for multiple stream creations. [364]
FServer := TAuraTABFileServer.Create('\\COFFEE\TickData\Pepperstone');
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);
FRandom.Free;
end;
procedure TForm1.LayoutResized(Sender: TObject);
begin
LogMemo.Lines.Add('resize');
end;
procedure TForm1.LoadButtonClick(Sender: TObject);
begin
if SymbolsComboBox.ItemIndex < 0 then
exit;
var Layout := CurrLayout<TVertScrollBox>;
if Layout = nil then
exit;
var Stream := FServer.CreateStream(FSymbols.WaitFor[SymbolsComboBox.ItemIndex]);
var Data := TDataStreamProvider.Create<TAuraAskBidFileItem>(30000, 10000, Stream);
var path := TPath.Create(Self);
path.Parent := Layout;
path.Align := TAlignLayout.None;
path.ProcessSignal(
Data.Changed,
procedure
begin
var Prices := Data.Value;
if Prices.Count > 0 then
begin
path.BeginUpdate;
try
path.Data.Clear;
path.Data.MoveTo(PointF(path.Width - 1, Prices[0].Data.Ask));
for var i := 1 to Prices.Count - 1 do
path.Data.LineTo(PointF(path.Width - i - 1, Prices[i].Data.Ask));
finally
path.EndUpdate;
end;
end;
end
);
end;
procedure TForm1.RandomButtonClick(Sender: TObject);
begin
var Layout := CurrLayout<TVertScrollBox>;
if Layout = nil then
exit;
var rnd: TRndItem;
rnd.Stream := FServer.CreateStream(FSymbols.WaitFor[Random(Length(FSymbols.WaitFor))]);
rnd.Data := TDataStreamProvider.Create<TAuraAskBidFileItem>(3000, 1000, rnd.Stream);
rnd.Labl := TLabel.Create(Self);
rnd.Labl.Parent := Layout;
rnd.Labl.Align := TAlignLayout.None;
rnd.Labl.WordWrap := false;
rnd.Labl.Width := 300;
rnd.Labl.Text := rnd.Stream.Symbol + ' loading...';
FRandom.Add(rnd);
var Proc :=
procedure(idx: Integer)
begin
FRandom[idx]
.Labl
.ProcessSignal(
FRandom[idx].Data.Changed,
procedure
begin
var data := FRandom[idx].Data.Value;
if data.Count > 0 then
begin
var dp := data[0];
FRandom[idx].Labl.Text :=
FRandom[idx].Stream.Symbol
+ ' '
+ dp.Time.ToString
+ ' '
+ data.TotalCount.ToString
+ ' '
+ dp.Data.Ask.ToString;
end;
end);
end;
Proc(FRandom.Count - 1);
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<TVertScrollBox>;
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;
procedure TForm1.ChartButtonClick(Sender: TObject);
begin
var Layout := CurrLayout<TVertScrollBox>;
if Layout = nil then
exit;
var Symbol := SelectedSymbol;
if Symbol = '' then
exit;
var currPathData := TWriteable<IObjectRef<TPathData>>.CreateWriteable.Protect;
var currLog := TWriteable<String>.CreateWriteable.Protect;
const width = 300;
var terminated := TFlag.CreateObserver(FTerminate.Signal).State;
var done :=
TaskManager.RunTask(
nil,
function: TState
begin
var Prices := TDataSeries<TAuraAskBidFileItem>.CreateDataSeries(width);
Result :=
FServer.ProcessData(
Symbol,
terminated,
TMycGenericProcessor<TArray<TDataPoint<TAuraAskBidFileItem>>>.Create(
function(const Values: TArray<TDataPoint<TAuraAskBidFileItem>>): TState
begin
Result := TState.Null;
Prices := Prices.Add(Values);
currLog.Value := Prices.TotalCount.ToString;
var PathData := TPathData.Create;
if Prices.Count > 0 then
begin
var n := Prices.Count;
PathData.MoveTo(PointF(n - 1, Prices[0].Data.Ask));
for var i := 1 to n - 1 do
PathData.LineTo(PointF(n - i - 1, Prices[i].Data.Ask));
for var i := n - 1 downto 0 do
PathData.LineTo(PointF(n - i - 1, Prices[i].Data.Bid));
end;
PathData.ClosePath;
currPathData.Value := TObjectRef<TPathData>.Create(PathData);
end
)
);
end
);
FProcessDone := TState.All([FProcessDone, done]);
var path := TPath.Create(Self);
path.Parent := Layout;
path.Align := TAlignLayout.None;
path.ProcessSignal(
currPathData.Changed,
procedure(out IsDone: Boolean)
begin
if currPathData.Value <> nil then
path.Data.Assign(currPathData.Value.Obj);
IsDone := done.IsSet;
end
);
end;
function TForm1.CurrLayout<T>: 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;
function TForm1.ExecuteStrategy(const Symbol: String; const Processor: IMycProcessor<TArray<TDataPoint<TAskBidItem>>>): TState;
var
dataProvider: IMycConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>;
begin
var terminated := TFlag.CreateObserver(FTerminate.Signal).State;
dataProvider :=
TMycGenericConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>.Create(
function(const Values: TArray<TDataPoint<TAuraAskBidFileItem>>): TArray<TDataPoint<TAskBidItem>>
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
);
dataProvider.Sender.Link(Processor);
Result := FServer.ProcessData(Symbol, terminated, dataProvider);
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;
end.