Polished DataProvider & Converter, RTTI field access
This commit is contained in:
@@ -5,7 +5,6 @@ uses
|
||||
System.StartUpCopy,
|
||||
FMX.Forms,
|
||||
MainForm in 'MainForm.pas' {Form1},
|
||||
Myc.Trade.Core.DataPoint in '..\Src\Myc.Trade.Core.DataPoint.pas',
|
||||
Myc.Aura.Module in '..\Src\Myc.Aura.Module.pas',
|
||||
Myc.Aura.Parameter in '..\Src\Myc.Aura.Parameter.pas',
|
||||
TestModule in 'TestModule.pas',
|
||||
@@ -14,7 +13,9 @@ uses
|
||||
Myc.Trade.DataArray in '..\Src\Myc.Trade.DataArray.pas',
|
||||
Myc.FMX.Chart.Series in '..\Src\Myc.FMX.Chart.Series.pas',
|
||||
Myc.Trade.Indicators in '..\Src\Myc.Trade.Indicators.pas',
|
||||
Myc.Trade.Types in '..\Src\Myc.Trade.Types.pas';
|
||||
Myc.Trade.Types in '..\Src\Myc.Trade.Types.pas',
|
||||
Myc.Trade.DataConverter in '..\Src\Myc.Trade.DataConverter.pas',
|
||||
Myc.Trade.Core.DataConverter in '..\Src\Myc.Trade.Core.DataConverter.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<ProjectVersion>20.3</ProjectVersion>
|
||||
<FrameworkType>FMX</FrameworkType>
|
||||
<Base>True</Base>
|
||||
<Config Condition="'$(Config)'==''">Debug</Config>
|
||||
<Config Condition="'$(Config)'==''">Release</Config>
|
||||
<Platform Condition="'$(Platform)'==''">Win64</Platform>
|
||||
<ProjectName Condition="'$(ProjectName)'==''">AuraTrader</ProjectName>
|
||||
<TargetedPlatforms>3</TargetedPlatforms>
|
||||
@@ -133,7 +133,6 @@
|
||||
<DCCReference Include="MainForm.pas">
|
||||
<Form>Form1</Form>
|
||||
</DCCReference>
|
||||
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Aura.Module.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Aura.Parameter.pas"/>
|
||||
<DCCReference Include="TestModule.pas"/>
|
||||
@@ -143,6 +142,8 @@
|
||||
<DCCReference Include="..\Src\Myc.FMX.Chart.Series.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Trade.Indicators.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Trade.Types.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Trade.DataConverter.pas"/>
|
||||
<DCCReference Include="..\Src\Myc.Trade.Core.DataConverter.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
|
||||
@@ -10,36 +10,12 @@ uses
|
||||
Myc.TaskManager,
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Trade.DataArray;
|
||||
Myc.Trade.DataArray,
|
||||
Myc.Trade.DataConverter,
|
||||
Myc.Trade.Core.DataConverter;
|
||||
|
||||
type
|
||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||
type
|
||||
TConvertFunc = reference to function(const Value: S): T;
|
||||
private
|
||||
FFunc: TConvertFunc;
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override;
|
||||
public
|
||||
constructor Create(const AFunc: TConvertFunc);
|
||||
end;
|
||||
|
||||
TIndicator<S, T> = class(TMycConverter<S, T>)
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override; final;
|
||||
function Calculate(const Value: S): T; virtual; abstract;
|
||||
end;
|
||||
|
||||
TGenericIndicator<S, T> = class(TIndicator<S, T>)
|
||||
private
|
||||
FFunc: TIndicatorFunc<S, T>;
|
||||
protected
|
||||
function Calculate(const Value: S): T; override; final;
|
||||
public
|
||||
constructor Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
end;
|
||||
|
||||
TTicksToBars = class(TMycConverter<TDataPoint<TAskBidItem>, TDataPoint<TOhlcItem>>)
|
||||
TTickAggregation = class(TMycConverter<TDataPoint<Double>, TDataPoint<TOhlcItem>>)
|
||||
private
|
||||
FTimeframe: TTimeframe;
|
||||
FCurrentBar: TDataPoint<TOhlcItem>;
|
||||
@@ -48,64 +24,26 @@ type
|
||||
function GetTimeframe: TTimeframe;
|
||||
public
|
||||
constructor Create(const ATimeframe: TTimeframe);
|
||||
function ProcessData(const Value: TDataPoint<TAskBidItem>): TState; override;
|
||||
function ProcessData(const Value: TDataPoint<Double>): TState; override;
|
||||
property CurrentBar: TDataPoint<TOhlcItem> read GetCurrentBar;
|
||||
property Timeframe: TTimeframe read GetTimeframe;
|
||||
end;
|
||||
|
||||
TTicker<T> = class(TMycConverter<TArray<T>, T>)
|
||||
public
|
||||
function ProcessData(const Values: TArray<T>): TState; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.DateUtils,
|
||||
System.Math;
|
||||
|
||||
{ TMycGenericConverter<S, T> }
|
||||
{ TTickAggregation }
|
||||
|
||||
constructor TMycGenericConverter<S, T>.Create(const AFunc: TConvertFunc);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
end;
|
||||
|
||||
function TMycGenericConverter<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := Broadcast(FFunc(Value));
|
||||
end;
|
||||
|
||||
{ TIndicator<S,T> }
|
||||
|
||||
function TIndicator<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := Broadcast(Calculate(Value));
|
||||
end;
|
||||
|
||||
{ TGenericIndicator<S,T> }
|
||||
|
||||
constructor TGenericIndicator<S, T>.Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
end;
|
||||
|
||||
function TGenericIndicator<S, T>.Calculate(const Value: S): T;
|
||||
begin
|
||||
Result := FFunc(Value);
|
||||
end;
|
||||
|
||||
{ TTicksToBars }
|
||||
|
||||
constructor TTicksToBars.Create(const ATimeframe: TTimeframe);
|
||||
constructor TTickAggregation.Create(const ATimeframe: TTimeframe);
|
||||
begin
|
||||
inherited Create;
|
||||
FTimeframe := ATimeframe;
|
||||
end;
|
||||
|
||||
function TTicksToBars.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
function TTickAggregation.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
var
|
||||
baseTime: TDateTime;
|
||||
begin
|
||||
@@ -153,25 +91,21 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TTicksToBars.GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
function TTickAggregation.GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||
begin
|
||||
Result := FCurrentBar;
|
||||
end;
|
||||
|
||||
function TTicksToBars.GetTimeframe: TTimeframe;
|
||||
function TTickAggregation.GetTimeframe: TTimeframe;
|
||||
begin
|
||||
Result := FTimeframe;
|
||||
end;
|
||||
|
||||
function TTicksToBars.ProcessData(const Value: TDataPoint<TAskBidItem>): TState;
|
||||
function TTickAggregation.ProcessData(const Value: TDataPoint<Double>): TState;
|
||||
var
|
||||
midPrice: Single;
|
||||
barStartTime: TDateTime;
|
||||
lastBarTime: TDateTime;
|
||||
currentBar: TOhlcItem;
|
||||
begin
|
||||
midPrice := (Value.Data.Ask + Value.Data.Bid) / 2;
|
||||
|
||||
// Update bar for the strategy's timeframe
|
||||
barStartTime := GetBarStartTime(Value.Time, FTimeframe);
|
||||
lastBarTime := FCurrentBar.Time;
|
||||
@@ -185,32 +119,24 @@ begin
|
||||
end;
|
||||
|
||||
// Start a new bar, Volume is 1 because this is the first tick.
|
||||
currentBar := TOhlcItem.Create(midPrice, midPrice, midPrice, midPrice, 1);
|
||||
FCurrentBar.Data := currentBar;
|
||||
FCurrentBar.Data.Open := Value.Data;
|
||||
FCurrentBar.Data.High := Value.Data;
|
||||
FCurrentBar.Data.Low := Value.Data;
|
||||
FCurrentBar.Data.Close := Value.Data;
|
||||
FCurrentBar.Data.Volume := 1;
|
||||
FCurrentBar.Time := barStartTime;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Update the currently aggregating bar
|
||||
currentBar := FCurrentBar.Data;
|
||||
currentBar.High := Max(currentBar.High, midPrice);
|
||||
currentBar.Low := Min(currentBar.Low, midPrice);
|
||||
currentBar.Close := midPrice;
|
||||
if Value.Data > FCurrentBar.Data.High then
|
||||
FCurrentBar.Data.High := Value.Data;
|
||||
if Value.Data < FCurrentBar.Data.Low then
|
||||
FCurrentBar.Data.Low := Value.Data;
|
||||
FCurrentBar.Data.Close := Value.Data;
|
||||
// Volume is the number of ticks needed to build the complete bar.
|
||||
currentBar.Volume := currentBar.Volume + 1;
|
||||
FCurrentBar.Data := currentBar;
|
||||
FCurrentBar.Data.Volume := FCurrentBar.Data.Volume + 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TTicker<T>.ProcessData(const Values: TArray<T>): TState;
|
||||
begin
|
||||
var done := TLatch.CreateLatch( Length(Values) );
|
||||
|
||||
// Process each incoming data point
|
||||
for var i:=0 to High(Values) do
|
||||
Broadcast(Values[i]).Signal.Subscribe(done);
|
||||
|
||||
Result := done.State;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -146,6 +146,16 @@ object Form1: TForm1
|
||||
TextSettings.Trimming = None
|
||||
OnClick = StopButtonClick
|
||||
end
|
||||
object Strat2Button: TSpeedButton
|
||||
Align = FitLeft
|
||||
Position.X = 639.119262695312500000
|
||||
Size.Width = 123.636352539062500000
|
||||
Size.Height = 34.000000000000000000
|
||||
Size.PlatformDefault = False
|
||||
Text = 'Strat 2'
|
||||
TextSettings.Trimming = None
|
||||
OnClick = Strat2ButtonClick
|
||||
end
|
||||
end
|
||||
end
|
||||
object ObjectsPanel: TPanel
|
||||
|
||||
+128
-15
@@ -30,6 +30,7 @@ uses
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataStream,
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Trade.DataConverter,
|
||||
Myc.Signals,
|
||||
Myc.Mutable,
|
||||
Myc.Signals.FMX,
|
||||
@@ -74,11 +75,13 @@ type
|
||||
TestPopup: TPopup;
|
||||
FlowLayout: TFlowLayout;
|
||||
StrategyButton: TSpeedButton;
|
||||
Strat2Button: TSpeedButton;
|
||||
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
|
||||
@@ -279,12 +282,12 @@ end;
|
||||
|
||||
function TForm1.ExecuteStrategy(const Symbol: String; const Processor: IMycProcessor<TArray<TDataPoint<TAskBidItem>>>): TState;
|
||||
var
|
||||
dataProvider: IMycConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>;
|
||||
dataProvider: TConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>;
|
||||
begin
|
||||
var terminated := TFlag.CreateObserver(FTerminate.Signal).State;
|
||||
|
||||
dataProvider :=
|
||||
TMycGenericConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>.Create(
|
||||
TConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>.CreateGeneric(
|
||||
function(const Values: TArray<TDataPoint<TAuraAskBidFileItem>>): TArray<TDataPoint<TAskBidItem>>
|
||||
begin
|
||||
SetLength(Result, Length(Values));
|
||||
@@ -328,32 +331,141 @@ begin
|
||||
|
||||
/////
|
||||
|
||||
var timeframe := TTimeframe.S15;
|
||||
var timeframe := TTimeframe.H4;
|
||||
|
||||
var ticker := TTicker<TDataPoint<TAskBidItem>>.Create;
|
||||
var ticker := TConverter.CreateTicker<TDataPoint<TAskBidItem>>;
|
||||
|
||||
var OhlcPoint := TTicksToBars.Create(timeframe);
|
||||
ticker.Sender.Link(OhlcPoint);
|
||||
var lastPrice :=
|
||||
ticker.Chain<TDataPoint<Double>>(
|
||||
function(const Tick: TDataPoint<TAskBidItem>): TDataPoint<Double>
|
||||
begin
|
||||
Result.Time := Tick.Time;
|
||||
Result.Data := 0.5 * (Tick.Data.Ask + Tick.Data.Bid);
|
||||
end
|
||||
);
|
||||
|
||||
var Timestamps: IMycConverter<TDataPoint<TOhlcItem>, TDateTime> :=
|
||||
TMycGenericConverter<TDataPoint<TOhlcItem>, TDateTime>
|
||||
.Create(function(const Ohlc: TDataPoint<TOhlcItem>): TDateTime begin Result := Ohlc.Time; end);
|
||||
var OhlcPoint := lastPrice.Chain<TDataPoint<TOhlcItem>>(TTickAggregation.Create(timeframe));
|
||||
|
||||
var Timestamps := OhlcPoint.Chain<TDateTime>(function(const Ohlc: TDataPoint<TOhlcItem>): TDateTime begin Result := Ohlc.Time; end);
|
||||
|
||||
var Ohlc := OhlcPoint.Chain<TOhlcItem>(function(const Ohlc: TDataPoint<TOhlcItem>): TOhlcItem begin Result := Ohlc.Data; end);
|
||||
|
||||
var Closes := Ohlc.Chain<Double>(function(const Ohlc: TOhlcItem): Double begin Result := Ohlc.Close; end);
|
||||
|
||||
var Hull := Closes.Chain<Double>(TIndicators.CreateHMA(150));
|
||||
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(50));
|
||||
var Ema := Closes.Chain<Double>(TIndicators.CreateEMA(21));
|
||||
var Boli := Closes.Chain<TBollingerBandsResult>(TIndicators.CreateBollingerBands(20, 2.0));
|
||||
var Rsi := Closes.Chain<Double>(TIndicators.CreateRSI(14));
|
||||
var Macd := Closes.Chain<TMacdResult>(TIndicators.CreateMACD(12, 26, 9));
|
||||
var Stoch := Ohlc.Chain<TStochasticResult>(TIndicators.CreateStochastic(14, 3));
|
||||
|
||||
chart.SetXAxisSeries(timeframe, Timestamps.Sender);
|
||||
|
||||
var Panel := chart.AddPanel;
|
||||
Panel.AddOhlcSeries(Ohlc.Sender);
|
||||
Panel.AddDoubleSeries(Hull.Sender, TAlphaColors.Aliceblue);
|
||||
Panel.AddDoubleSeries(Sma.Sender, TAlphaColors.Yellow);
|
||||
Panel.AddDoubleSeries(Ema.Sender, TAlphaColors.Aqua);
|
||||
Panel.AddDoubleSeries(Boli.Field<Double>('UpperBand').Sender, TAlphaColors.Gray);
|
||||
Panel.AddDoubleSeries(Boli.Field<Double>('MiddleBand').Sender, TAlphaColors.Darkgray, 1.0);
|
||||
Panel.AddDoubleSeries(Boli.Field<Double>('LowerBand').Sender, TAlphaColors.Gray);
|
||||
|
||||
Panel := chart.AddPanel;
|
||||
Panel.AddDoubleSeries(Rsi.Sender, TAlphaColors.Fuchsia);
|
||||
|
||||
Panel := chart.AddPanel;
|
||||
Panel.AddDoubleSeries(Macd.Field<Double>('MacdLine').Sender, TAlphaColors.Orange);
|
||||
Panel.AddDoubleSeries(Macd.Field<Double>('SignalLine').Sender, TAlphaColors.Dodgerblue);
|
||||
Panel.AddDoubleSeries(Macd.Field<Double>('Histogram').Sender, TAlphaColors.Lightgreen);
|
||||
|
||||
Panel := chart.AddPanel;
|
||||
Panel.AddDoubleSeries(Stoch.Field<Double>('K').Sender, TAlphaColors.Green);
|
||||
Panel.AddDoubleSeries(Stoch.Field<Double>('D').Sender, TAlphaColors.Red);
|
||||
|
||||
/////
|
||||
|
||||
var tickChart := TMycChart.Create(Self);
|
||||
tickChart.Height := Layout.ChildrenRect.Width * 9 / 16;
|
||||
AlignControl(tickChart);
|
||||
tickChart.Lookback.Value := 1000000;
|
||||
|
||||
var TickTime := ticker.Field<TDateTime>('Time');
|
||||
|
||||
var TickData := ticker.Field<TAskBidItem>('Data');
|
||||
var TickAsk := TickData.Field<Double>('Ask');
|
||||
var TickBid := TickData.Field<Double>('Bid');
|
||||
|
||||
var TickSpread := TickData.Chain<Double>(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);
|
||||
}
|
||||
/////
|
||||
|
||||
var done := ExecuteStrategy(Symbol, ticker);
|
||||
FProcessDone := TState.All([FProcessDone, done]);
|
||||
end;
|
||||
|
||||
procedure TForm1.Strat2ButtonClick(Sender: TObject);
|
||||
begin
|
||||
var timeframe := TTimeframe.M15;
|
||||
|
||||
var ticker := TConverter.CreateTicker<TDataPoint<TAskBidItem>>;
|
||||
|
||||
var lastPrice :=
|
||||
ticker.Chain<TDataPoint<Double>>(
|
||||
function(const Tick: TDataPoint<TAskBidItem>): TDataPoint<Double>
|
||||
begin
|
||||
Result.Time := Tick.Time;
|
||||
Result.Data := 0.5 * (Tick.Data.Ask + Tick.Data.Bid);
|
||||
end
|
||||
);
|
||||
|
||||
var OhlcPoint := TTickAggregation.Create(timeframe);
|
||||
lastPrice.Sender.Link(OhlcPoint);
|
||||
|
||||
var Closes :=
|
||||
TConverter<TDataPoint<TOhlcItem>, Double>
|
||||
.CreateGeneric(function(const Ohlc: TDataPoint<TOhlcItem>): Double begin Result := Ohlc.Data.Close; end);
|
||||
|
||||
var Hull := TConverter<Double, Double>.CreateGeneric(TIndicators.CreateHMA(150));
|
||||
|
||||
var Timestamps :=
|
||||
TConverter<TDataPoint<TOhlcItem>, TDateTime>
|
||||
.CreateGeneric(function(const Ohlc: TDataPoint<TOhlcItem>): TDateTime begin Result := Ohlc.Time; end);
|
||||
OhlcPoint.Sender.Link(TimeStamps);
|
||||
|
||||
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;
|
||||
|
||||
/////
|
||||
|
||||
{
|
||||
|
||||
chart.SetXAxisSeries(timeframe, Timestamps.Sender);
|
||||
|
||||
var Panel := chart.AddPanel;
|
||||
|
||||
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);
|
||||
Panel.AddOhlcSeries(Ohlc.Sender);
|
||||
|
||||
var Closes: IMycConverter<TOhlcItem, Double> :=
|
||||
TMycGenericConverter<TOhlcItem, Double>.Create(function(const Ohlc: TOhlcItem): Double begin Result := Ohlc.Close; end);
|
||||
|
||||
Ohlc.Sender.Link(Closes);
|
||||
|
||||
var Hull: IMycConverter<Double, Double> := TGenericIndicator<Double, Double>.Create(TIndicators.CreateHMA(150));
|
||||
@@ -477,6 +589,7 @@ begin
|
||||
|
||||
var done := ExecuteStrategy(Symbol, ticker);
|
||||
FProcessDone := TState.All([FProcessDone, done]);
|
||||
}
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user