Process.Update for synchronization
This commit is contained in:
@@ -6,7 +6,6 @@ uses
|
|||||||
FMX.Forms,
|
FMX.Forms,
|
||||||
MainForm in 'MainForm.pas' {Form1},
|
MainForm in 'MainForm.pas' {Form1},
|
||||||
Myc.Trade.Core.DataPoint in '..\Src\Myc.Trade.Core.DataPoint.pas',
|
Myc.Trade.Core.DataPoint in '..\Src\Myc.Trade.Core.DataPoint.pas',
|
||||||
Myc.Trade.Ticker in '..\Src\Myc.Trade.Ticker.pas',
|
|
||||||
Myc.Aura.Module in '..\Src\Myc.Aura.Module.pas',
|
Myc.Aura.Module in '..\Src\Myc.Aura.Module.pas',
|
||||||
Myc.Aura.Parameter in '..\Src\Myc.Aura.Parameter.pas',
|
Myc.Aura.Parameter in '..\Src\Myc.Aura.Parameter.pas',
|
||||||
TestModule in 'TestModule.pas',
|
TestModule in 'TestModule.pas',
|
||||||
|
|||||||
@@ -134,7 +134,6 @@
|
|||||||
<Form>Form1</Form>
|
<Form>Form1</Form>
|
||||||
</DCCReference>
|
</DCCReference>
|
||||||
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.pas"/>
|
<DCCReference Include="..\Src\Myc.Trade.Core.DataPoint.pas"/>
|
||||||
<DCCReference Include="..\Src\Myc.Trade.Ticker.pas"/>
|
|
||||||
<DCCReference Include="..\Src\Myc.Aura.Module.pas"/>
|
<DCCReference Include="..\Src\Myc.Aura.Module.pas"/>
|
||||||
<DCCReference Include="..\Src\Myc.Aura.Parameter.pas"/>
|
<DCCReference Include="..\Src\Myc.Aura.Parameter.pas"/>
|
||||||
<DCCReference Include="TestModule.pas"/>
|
<DCCReference Include="TestModule.pas"/>
|
||||||
@@ -185,12 +184,6 @@
|
|||||||
<Overwrite>true</Overwrite>
|
<Overwrite>true</Overwrite>
|
||||||
</Platform>
|
</Platform>
|
||||||
</DeployFile>
|
</DeployFile>
|
||||||
<DeployFile LocalName="Win64\Debug\AuraTrader.rsm" Configuration="Debug" Class="DebugSymbols">
|
|
||||||
<Platform Name="Win64">
|
|
||||||
<RemoteName>AuraTrader.rsm</RemoteName>
|
|
||||||
<Overwrite>true</Overwrite>
|
|
||||||
</Platform>
|
|
||||||
</DeployFile>
|
|
||||||
<DeployFile LocalName="Win64\Release\AuraTrader.exe" Configuration="Release" Class="ProjectOutput">
|
<DeployFile LocalName="Win64\Release\AuraTrader.exe" Configuration="Release" Class="ProjectOutput">
|
||||||
<Platform Name="Win64">
|
<Platform Name="Win64">
|
||||||
<RemoteName>AuraTrader.exe</RemoteName>
|
<RemoteName>AuraTrader.exe</RemoteName>
|
||||||
|
|||||||
Binary file not shown.
+85
-228
@@ -7,49 +7,28 @@ uses
|
|||||||
Myc.Signals,
|
Myc.Signals,
|
||||||
Myc.Lazy,
|
Myc.Lazy,
|
||||||
Myc.Trade.DataPoint,
|
Myc.Trade.DataPoint,
|
||||||
Myc.Trade.DataArray,
|
Myc.Trade.DataArray;
|
||||||
Myc.Core.Notifier;
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TTimeframe = (M1, M5, H1, D);
|
TTimeframe = (M1, M5, H1, D);
|
||||||
|
|
||||||
TTag = Pointer;
|
|
||||||
|
|
||||||
IMycBroadcast<T> = interface
|
|
||||||
function Link(const Strategy: IMycProcessor<T>): TTag;
|
|
||||||
procedure Unlink(Tag: TTag);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// A contained object that broadcasts Values to linked strategies
|
|
||||||
TMycBroadcast<T> = class(TContainedObject, IMycBroadcast<T>)
|
|
||||||
private
|
|
||||||
FLinkedStrategies: TMycNotifyList<IMycProcessor<T>>;
|
|
||||||
protected
|
|
||||||
// Link a strategy
|
|
||||||
function Link(const Strategy: IMycProcessor<T>): TTag;
|
|
||||||
// Unlink a linked strategy
|
|
||||||
procedure Unlink(Tag: TTag);
|
|
||||||
// Broadcasts the given data points to all linked strategies.
|
|
||||||
procedure Broadcast(const Value: T);
|
|
||||||
public
|
|
||||||
constructor Create(const Controller: IInterface);
|
|
||||||
destructor Destroy; override;
|
|
||||||
end;
|
|
||||||
|
|
||||||
IMycConverter<S, T> = interface(IMycProcessor<S>)
|
IMycConverter<S, T> = interface(IMycProcessor<S>)
|
||||||
function GetObservers: IMycBroadcast<T>;
|
function GetSender: IMycDataProvider<T>;
|
||||||
property Observers: IMycBroadcast<T> read GetObservers;
|
property Sender: IMycDataProvider<T> read GetSender;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, IMycConverter<S, T>)
|
TMycConverter<S, T> = class abstract(TMycProcessor<S>, IMycConverter<S, T>)
|
||||||
private
|
private
|
||||||
FObservers: TMycBroadcast<T>;
|
FSender: TMycDataProvider<T>;
|
||||||
|
function GetSender: IMycDataProvider<T>;
|
||||||
protected
|
protected
|
||||||
|
// Broadcasts the given data to all linked processors.
|
||||||
procedure Broadcast(const Value: T);
|
procedure Broadcast(const Value: T);
|
||||||
function GetObservers: IMycBroadcast<T>;
|
procedure Update; override;
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
property Sender: IMycDataProvider<T> read GetSender;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||||
@@ -63,40 +42,7 @@ type
|
|||||||
constructor Create(const AFunc: TConvertFunc);
|
constructor Create(const AFunc: TConvertFunc);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Series
|
ITicksToTimeframe = interface(IMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>>)
|
||||||
|
|
||||||
IMycSeriesConverter<S, T> = interface(IMycConverter<TArray<S>, T>)
|
|
||||||
function GetLookback: Integer;
|
|
||||||
property Lookback: Integer read GetLookback;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Indicator
|
|
||||||
|
|
||||||
IMycIndicator<S, T> = interface(IMycSeriesConverter<S, TArray<T>>)
|
|
||||||
end;
|
|
||||||
|
|
||||||
TMycIndicator<S, T> = class abstract(TMycConverter<TArray<S>, TArray<T>>, IMycIndicator<S, T>)
|
|
||||||
protected
|
|
||||||
function GetLookback: Integer; virtual; abstract;
|
|
||||||
function ProcessData(const Value: TArray<S>): Boolean; override; abstract;
|
|
||||||
public
|
|
||||||
property Lookback: Integer read GetLookback;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TMycGenericIndicator<S, T> = class(TMycIndicator<S, T>)
|
|
||||||
type
|
|
||||||
TConvertFunc = reference to function(const Value: S): T;
|
|
||||||
private
|
|
||||||
FLookback: Integer;
|
|
||||||
FFunc: TConvertFunc;
|
|
||||||
protected
|
|
||||||
function GetLookback: Integer; override;
|
|
||||||
function ProcessData(const Value: TArray<S>): Boolean; override;
|
|
||||||
public
|
|
||||||
constructor Create(ALookback: Integer; const AFunc: TConvertFunc);
|
|
||||||
end;
|
|
||||||
|
|
||||||
ITicksToTimeframe = interface(IMycConverter<TArray<TDataPoint<TAskBidItem>>, TArray<TDataPoint<TOhlcItem>>>)
|
|
||||||
function GetCurrentBar: TDataPoint<TOhlcItem>;
|
function GetCurrentBar: TDataPoint<TOhlcItem>;
|
||||||
function GetStateText: TWriteable<String>;
|
function GetStateText: TWriteable<String>;
|
||||||
function GetTimeframe: TTimeframe;
|
function GetTimeframe: TTimeframe;
|
||||||
@@ -105,7 +51,7 @@ type
|
|||||||
property Timeframe: TTimeframe read GetTimeframe;
|
property Timeframe: TTimeframe read GetTimeframe;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TTicksToTimeframe = class(TMycConverter<TArray<TDataPoint<TAskBidItem>>, TArray<TDataPoint<TOhlcItem>>>, ITicksToTimeframe)
|
TTicksToTimeframe = class(TMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>>, ITicksToTimeframe)
|
||||||
private
|
private
|
||||||
FTimeframe: TTimeframe;
|
FTimeframe: TTimeframe;
|
||||||
FStateText: TWriteable<String>;
|
FStateText: TWriteable<String>;
|
||||||
@@ -127,7 +73,7 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Implements the Hull Moving Average indicator.
|
// Implements the Hull Moving Average indicator.
|
||||||
THullMovingAverage = class(TMycIndicator<Double, Double>)
|
THullMovingAverage = class(TMycConverter<Double, Double>)
|
||||||
private
|
private
|
||||||
FPeriod: Integer;
|
FPeriod: Integer;
|
||||||
FPeriodHalf: Integer;
|
FPeriodHalf: Integer;
|
||||||
@@ -139,8 +85,7 @@ type
|
|||||||
// Calculates the Weighted Moving Average for the most recent data.
|
// Calculates the Weighted Moving Average for the most recent data.
|
||||||
function CalculateWMA(const Series: TMycDataArray<Double>; const Period: Integer): Double;
|
function CalculateWMA(const Series: TMycDataArray<Double>; const Period: Integer): Double;
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Values: TArray<Double>): Boolean; override;
|
function ProcessData(const Value: Double): Boolean; override;
|
||||||
function GetLookback: Integer; override;
|
|
||||||
public
|
public
|
||||||
constructor Create(const APeriod: Integer);
|
constructor Create(const APeriod: Integer);
|
||||||
end;
|
end;
|
||||||
@@ -152,57 +97,6 @@ uses
|
|||||||
System.DateUtils,
|
System.DateUtils,
|
||||||
System.Math;
|
System.Math;
|
||||||
|
|
||||||
{ TMycBroadcast<T> }
|
|
||||||
|
|
||||||
constructor TMycBroadcast<T>.Create(const Controller: IInterface);
|
|
||||||
begin
|
|
||||||
inherited Create(Controller);
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TMycBroadcast<T>.Destroy;
|
|
||||||
begin
|
|
||||||
FLinkedStrategies.Finalize;
|
|
||||||
inherited Destroy;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMycBroadcast<T>.Broadcast(const Value: T);
|
|
||||||
begin
|
|
||||||
var cValue := Value;
|
|
||||||
|
|
||||||
FLinkedStrategies.Lock;
|
|
||||||
try
|
|
||||||
FLinkedStrategies.Notify(
|
|
||||||
function(const Processor: IMycProcessor<T>): Boolean
|
|
||||||
begin
|
|
||||||
Result := Processor.ProcessData(cValue);
|
|
||||||
end
|
|
||||||
);
|
|
||||||
finally
|
|
||||||
FLinkedStrategies.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycBroadcast<T>.Link(const Strategy: IMycProcessor<T>): TTag;
|
|
||||||
begin
|
|
||||||
// Add the strategy to the notification list
|
|
||||||
FLinkedStrategies.Lock;
|
|
||||||
try
|
|
||||||
Result := FLinkedStrategies.Advise(Strategy);
|
|
||||||
finally
|
|
||||||
FLinkedStrategies.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TMycBroadcast<T>.Unlink(Tag: TTag);
|
|
||||||
begin
|
|
||||||
FLinkedStrategies.Lock;
|
|
||||||
try
|
|
||||||
FLinkedStrategies.Unadvise(Tag);
|
|
||||||
finally
|
|
||||||
FLinkedStrategies.Release;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TTicksToTimeframe }
|
{ TTicksToTimeframe }
|
||||||
|
|
||||||
constructor TTicksToTimeframe.Create(const ATimeframe: TTimeframe; const AStateText: TWriteable<String>);
|
constructor TTicksToTimeframe.Create(const ATimeframe: TTimeframe; const AStateText: TWriteable<String>);
|
||||||
@@ -247,54 +141,47 @@ var
|
|||||||
barStartTime: TDateTime;
|
barStartTime: TDateTime;
|
||||||
lastBarTime: TDateTime;
|
lastBarTime: TDateTime;
|
||||||
currentBar: TOhlcItem;
|
currentBar: TOhlcItem;
|
||||||
producedBars: TList<TDataPoint<TOhlcItem>>;
|
|
||||||
begin
|
begin
|
||||||
Result := true;
|
Result := true;
|
||||||
producedBars := TList<TDataPoint<TOhlcItem>>.Create;
|
|
||||||
try
|
// Process each incoming data point
|
||||||
// Process each incoming data point
|
for point in Values do
|
||||||
for point in Values do
|
begin
|
||||||
|
midPrice := (point.Data.Ask + point.Data.Bid) / 2;
|
||||||
|
|
||||||
|
// Update bar for the strategy's timeframe
|
||||||
|
barStartTime := GetBarStartTime(point.Time, FTimeframe);
|
||||||
|
lastBarTime := FCurrentBar.Time;
|
||||||
|
|
||||||
|
if (barStartTime > lastBarTime) then
|
||||||
begin
|
begin
|
||||||
midPrice := (point.Data.Ask + point.Data.Bid) / 2;
|
// A new bar starts, so the previous one is now complete.
|
||||||
|
if (lastBarTime > 0) then
|
||||||
// Update bar for the strategy's timeframe
|
|
||||||
barStartTime := GetBarStartTime(point.Time, FTimeframe);
|
|
||||||
lastBarTime := FCurrentBar.Time;
|
|
||||||
|
|
||||||
if (barStartTime > lastBarTime) then
|
|
||||||
begin
|
begin
|
||||||
// A new bar starts, so the previous one is now complete.
|
Broadcast(FCurrentBar);
|
||||||
if (lastBarTime > 0) then
|
|
||||||
begin
|
|
||||||
producedBars.Add(FCurrentBar);
|
|
||||||
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.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;
|
|
||||||
// Volume is the number of ticks needed to build the complete bar.
|
|
||||||
currentBar.Volume := currentBar.Volume + 1;
|
|
||||||
FCurrentBar.Data := currentBar;
|
|
||||||
end;
|
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.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;
|
||||||
|
// Volume is the number of ticks needed to build the complete bar.
|
||||||
|
currentBar.Volume := currentBar.Volume + 1;
|
||||||
|
FCurrentBar.Data := currentBar;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
with FCurrentBar do
|
|
||||||
FStateText.Value :=
|
|
||||||
Format('Cuur Bar: O:%.5f H:%.5f L:%.5f C:%.5f V:%.0f', [Data.Open, Data.High, Data.Low, Data.Close, Data.Volume]);
|
|
||||||
|
|
||||||
Broadcast(producedBars.ToArray);
|
|
||||||
finally
|
|
||||||
producedBars.Free;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
with FCurrentBar do
|
||||||
|
FStateText.Value :=
|
||||||
|
Format('Cuur Bar: O:%.5f H:%.5f L:%.5f C:%.5f V:%.0f', [Data.Open, Data.High, Data.Low, Data.Close, Data.Volume]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ THullMovingAverage }
|
{ THullMovingAverage }
|
||||||
@@ -337,62 +224,43 @@ begin
|
|||||||
Result := numerator / denominator;
|
Result := numerator / denominator;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function THullMovingAverage.GetLookback: Integer;
|
function THullMovingAverage.ProcessData(const Value: Double): Boolean;
|
||||||
begin
|
|
||||||
Result := FPeriod + FPeriodSqrt - 1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function THullMovingAverage.ProcessData(const Values: TArray<Double>): Boolean;
|
|
||||||
var
|
var
|
||||||
i: Integer;
|
|
||||||
price: Double;
|
price: Double;
|
||||||
wmaHalf, wmaFull, diff: Double;
|
wmaHalf, wmaFull, diff: Double;
|
||||||
hma: Double;
|
hma: Double;
|
||||||
resultArray: TArray<Double>;
|
|
||||||
begin
|
begin
|
||||||
Result := true;
|
Result := true;
|
||||||
|
|
||||||
// Pre-allocate the result array since its size is known in advance.
|
price := Value;
|
||||||
SetLength(resultArray, Length(Values));
|
|
||||||
|
|
||||||
for i := 0 to High(Values) do
|
// Default HMA to NaN for the warm-up period.
|
||||||
|
hma := Double.NaN;
|
||||||
|
|
||||||
|
// Add new price to the source data array, respecting the lookback period.
|
||||||
|
FSourceData := FSourceData.Add(price, FPeriod);
|
||||||
|
|
||||||
|
// Check if there is enough data to start the first stage of calculation.
|
||||||
|
if (FSourceData.Count >= FPeriod) then
|
||||||
begin
|
begin
|
||||||
price := Values[i];
|
// Calculate the two WMAs for the first step.
|
||||||
|
wmaHalf := CalculateWMA(FSourceData, FPeriodHalf);
|
||||||
|
wmaFull := CalculateWMA(FSourceData, FPeriod);
|
||||||
|
|
||||||
// Default HMA to NaN for the warm-up period.
|
// Calculate the difference and add to the intermediate series.
|
||||||
hma := Double.NaN;
|
diff := 2 * wmaHalf - wmaFull;
|
||||||
|
FDiffSeries := FDiffSeries.Add(diff, FPeriodSqrt);
|
||||||
|
|
||||||
// Add new price to the source data array, respecting the lookback period.
|
// Check if there is enough intermediate data for the final calculation.
|
||||||
FSourceData := FSourceData.Add(price, FPeriod);
|
if (FDiffSeries.Count >= FPeriodSqrt) then
|
||||||
|
|
||||||
// Check if there is enough data to start the first stage of calculation.
|
|
||||||
if (FSourceData.Count >= FPeriod) then
|
|
||||||
begin
|
begin
|
||||||
// Calculate the two WMAs for the first step.
|
// Calculate the final HMA value, overwriting the default 0.0.
|
||||||
wmaHalf := CalculateWMA(FSourceData, FPeriodHalf);
|
hma := CalculateWMA(FDiffSeries, FPeriodSqrt);
|
||||||
wmaFull := CalculateWMA(FSourceData, FPeriod);
|
|
||||||
|
|
||||||
// Calculate the difference and add to the intermediate series.
|
|
||||||
diff := 2 * wmaHalf - wmaFull;
|
|
||||||
FDiffSeries := FDiffSeries.Add(diff, FPeriodSqrt);
|
|
||||||
|
|
||||||
// Check if there is enough intermediate data for the final calculation.
|
|
||||||
if (FDiffSeries.Count >= FPeriodSqrt) then
|
|
||||||
begin
|
|
||||||
// Calculate the final HMA value, overwriting the default 0.0.
|
|
||||||
hma := CalculateWMA(FDiffSeries, FPeriodSqrt);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Assign the result (either the calculated HMA or 0.0) directly into the array.
|
|
||||||
resultArray[i] := hma;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Broadcast the result array if the input was not empty.
|
// Broadcast the result
|
||||||
if (Length(resultArray) > 0) then
|
Broadcast(hma);
|
||||||
begin
|
|
||||||
Broadcast(resultArray);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMycGenericConverter<S, T> }
|
{ TMycGenericConverter<S, T> }
|
||||||
@@ -409,51 +277,40 @@ begin
|
|||||||
Broadcast(FFunc(Value));
|
Broadcast(FFunc(Value));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TMycGenericIndicator<S, T>.Create(ALookback: Integer; const AFunc: TConvertFunc);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
FFunc := AFunc;
|
|
||||||
FLookback := ALookback;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycGenericIndicator<S, T>.GetLookback: Integer;
|
|
||||||
begin
|
|
||||||
Result := FLookback;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TMycGenericIndicator<S, T>.ProcessData(const Value: TArray<S>): Boolean;
|
|
||||||
var
|
|
||||||
Arr: TArray<T>;
|
|
||||||
begin
|
|
||||||
Result := true;
|
|
||||||
SetLength(Arr, Length(Value));
|
|
||||||
for var i := 0 to High(Arr) do
|
|
||||||
Arr[i] := FFunc(Value[i]);
|
|
||||||
Broadcast(Arr);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycConverter<S, T> }
|
{ TMycConverter<S, T> }
|
||||||
|
|
||||||
constructor TMycConverter<S, T>.Create;
|
constructor TMycConverter<S, T>.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FObservers := TMycBroadcast<T>.Create(Self);
|
FSender := TMycDataProvider<T>.Create(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TMycConverter<S, T>.Destroy;
|
destructor TMycConverter<S, T>.Destroy;
|
||||||
begin
|
begin
|
||||||
FObservers.Free;
|
FSender.Free;
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycConverter<S, T>.Broadcast(const Value: T);
|
procedure TMycConverter<S, T>.Broadcast(const Value: T);
|
||||||
begin
|
begin
|
||||||
FObservers.Broadcast(Value);
|
var cValue := Value;
|
||||||
|
FSender.Notify(function(const Processor: IMycProcessor<T>): Boolean begin Result := Processor.ProcessData(cValue) end);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycConverter<S, T>.GetObservers: IMycBroadcast<T>;
|
function TMycConverter<S, T>.GetSender: IMycDataProvider<T>;
|
||||||
begin
|
begin
|
||||||
Result := FObservers;
|
Result := FSender;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMycConverter<S, T>.Update;
|
||||||
|
begin
|
||||||
|
FSender.Notify(
|
||||||
|
function(const Processor: IMycProcessor<T>): Boolean
|
||||||
|
begin
|
||||||
|
Processor.Update;
|
||||||
|
Result := true
|
||||||
|
end
|
||||||
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+12
-11
@@ -165,34 +165,35 @@ begin
|
|||||||
var chart := TMycChart.Create(Self);
|
var chart := TMycChart.Create(Self);
|
||||||
AlignControl( chart );
|
AlignControl( chart );
|
||||||
chart.Height := Layout.ChildrenRect.Width*9/16;
|
chart.Height := Layout.ChildrenRect.Width*9/16;
|
||||||
|
chart.Lookback := 1000;
|
||||||
|
|
||||||
/////
|
/////
|
||||||
|
|
||||||
var strategy: ITicksToTimeframe := TTicksToTimeframe.Create( D, stateText );
|
var OhlcPoint: ITicksToTimeframe := TTicksToTimeframe.Create( H1, stateText );
|
||||||
|
|
||||||
var Ohlc: IMycIndicator<TDataPoint<TOhlcItem>, TOhlcItem> := TMycGenericIndicator<TDataPoint<TOhlcItem>, TOhlcItem>.Create( 0,
|
var Ohlc: IMycConverter<TDataPoint<TOhlcItem>, TOhlcItem> := TMycGenericConverter<TDataPoint<TOhlcItem>, TOhlcItem>.Create(
|
||||||
function( const Ohlc: TDataPoint<TOhlcItem> ): TOhlcItem
|
function( const Ohlc: TDataPoint<TOhlcItem> ): TOhlcItem
|
||||||
begin
|
begin
|
||||||
Result := Ohlc.Data;
|
Result := Ohlc.Data;
|
||||||
end );
|
end );
|
||||||
|
|
||||||
strategy.Observers.Link(Ohlc);
|
OhlcPoint.Sender.Link(Ohlc);
|
||||||
|
|
||||||
var Closes: IMycIndicator<TOhlcItem, Double> := TMycGenericIndicator<TOhlcItem, Double>.Create( 0,
|
var Closes: IMycConverter<TOhlcItem, Double> := TMycGenericConverter<TOhlcItem, Double>.Create(
|
||||||
function( const Ohlc: TOhlcItem ): Double
|
function( const Ohlc: TOhlcItem ): Double
|
||||||
begin
|
begin
|
||||||
Result := Ohlc.Close;
|
Result := Ohlc.Close;
|
||||||
end );
|
end );
|
||||||
|
|
||||||
Ohlc.Observers.Link( Closes );
|
Ohlc.Sender.Link( Closes );
|
||||||
|
|
||||||
var Hull: IMycIndicator<Double, Double> := THullMovingAverage.Create( 50 );
|
var Hull: IMycConverter<Double, Double> := THullMovingAverage.Create( 250 );
|
||||||
|
|
||||||
Closes.Observers.Link( Hull );
|
Closes.Sender.Link( Hull );
|
||||||
|
|
||||||
Hull.Observers.Link(chart.CreateDoubleListener);
|
chart.AddDoubleSeries( Hull.Sender );
|
||||||
|
|
||||||
var done := ExecuteStrategy( Symbol, strategy );
|
var done := ExecuteStrategy( Symbol, OhlcPoint );
|
||||||
|
|
||||||
/////
|
/////
|
||||||
|
|
||||||
@@ -205,7 +206,7 @@ begin
|
|||||||
|
|
||||||
FProcessDone := TState.All([FProcessDone, done]);
|
FProcessDone := TState.All([FProcessDone, done]);
|
||||||
|
|
||||||
strategy.Observers.Link(chart.CreateOhlcListener);
|
chart.AddOhlcSeries( OhlcPoint.Sender );
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm1.TreeViewDblClick(Sender: TObject);
|
procedure TForm1.TreeViewDblClick(Sender: TObject);
|
||||||
@@ -531,7 +532,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end );
|
end );
|
||||||
|
|
||||||
dataProvider.Observers.Link( Processor );
|
dataProvider.Sender.Link( Processor );
|
||||||
|
|
||||||
Result := FServer.ProcessData( Symbol, terminated, dataProvider );
|
Result := FServer.ProcessData( Symbol, terminated, dataProvider );
|
||||||
end;
|
end;
|
||||||
|
|||||||
+132
-72
@@ -15,17 +15,18 @@ uses
|
|||||||
FMX.Controls,
|
FMX.Controls,
|
||||||
FMX.Graphics,
|
FMX.Graphics,
|
||||||
Myc.Trade.DataPoint,
|
Myc.Trade.DataPoint,
|
||||||
Myc.Signals;
|
Myc.Signals,
|
||||||
|
Myc.Lazy;
|
||||||
|
|
||||||
type
|
type
|
||||||
TCandleStyle = (csCandleStick, csHiLoBar);
|
TCandleStyle = (csCandleStick, csHiLoBar);
|
||||||
|
|
||||||
TMycChart = class(TStyledControl)
|
TMycChart = class(TStyledControl)
|
||||||
type
|
type
|
||||||
TSeries = class abstract(TContainedObject)
|
TSeries = class abstract(TObject)
|
||||||
private
|
private
|
||||||
|
FOwner: TMycChart;
|
||||||
function GetMainSeries: TSeries;
|
function GetMainSeries: TSeries;
|
||||||
function GetOwner: TMycChart;
|
|
||||||
protected
|
protected
|
||||||
function GetCount: Int64; virtual; abstract;
|
function GetCount: Int64; virtual; abstract;
|
||||||
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; virtual; abstract;
|
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; virtual; abstract;
|
||||||
@@ -36,7 +37,7 @@ type
|
|||||||
constructor Create(AOwner: TMycChart);
|
constructor Create(AOwner: TMycChart);
|
||||||
property Count: Int64 read GetCount;
|
property Count: Int64 read GetCount;
|
||||||
property MainSeries: TSeries read GetMainSeries;
|
property MainSeries: TSeries read GetMainSeries;
|
||||||
property Owner: TMycChart read GetOwner;
|
property Owner: TMycChart read FOwner;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -51,15 +52,19 @@ type
|
|||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
// Creates an OHLC candlestick/bar series
|
// Creates an OHLC candlestick/bar series
|
||||||
function CreateOhlcListener(
|
procedure AddOhlcSeries(
|
||||||
|
const DataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||||
const AUpColor: TAlphaColor = TAlphaColors.Green;
|
const AUpColor: TAlphaColor = TAlphaColors.Green;
|
||||||
const ADownColor: TAlphaColor = TAlphaColors.Red;
|
const ADownColor: TAlphaColor = TAlphaColors.Red;
|
||||||
const AStyle: TCandleStyle = csCandleStick
|
const AStyle: TCandleStyle = csCandleStick
|
||||||
): IMycProcessor<TArray<TDataPoint<TOhlcItem>>>;
|
);
|
||||||
|
|
||||||
// Creates a simple line series for double values
|
// Creates a simple line series for double values
|
||||||
function CreateDoubleListener(const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue; const ALineWidth: Single = 1.5):
|
procedure AddDoubleSeries(
|
||||||
IMycProcessor<TArray<Double>>;
|
const DataProvider: IMycDataProvider<Double>;
|
||||||
|
const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue;
|
||||||
|
const ALineWidth: Single = 1.5
|
||||||
|
);
|
||||||
|
|
||||||
// The maximum number of data points to display from the main series.
|
// The maximum number of data points to display from the main series.
|
||||||
property Lookback: Integer read FLookback write FLookback;
|
property Lookback: Integer read FLookback write FLookback;
|
||||||
@@ -69,24 +74,42 @@ implementation
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
System.Math,
|
System.Math,
|
||||||
System.SyncObjs, WinApi.Windows,
|
System.SyncObjs,
|
||||||
|
WinApi.Windows,
|
||||||
Myc.Trade.DataArray;
|
Myc.Trade.DataArray;
|
||||||
|
|
||||||
type
|
type
|
||||||
TChartSeriesProcessor<T> = class(TMycChart.TSeries, IMycProcessor<TArray<T>>)
|
TChartSeriesReceiver<T> = class(TMycProcessor<T>)
|
||||||
|
strict private
|
||||||
|
FCurrData: TMycDataArray<T>;
|
||||||
|
FLookback: Int64;
|
||||||
|
private
|
||||||
|
FData: TWriteable<TMycDataArray<T>>;
|
||||||
|
protected
|
||||||
|
function ProcessData(const Value: T): Boolean; override;
|
||||||
|
procedure Update; override;
|
||||||
|
public
|
||||||
|
constructor Create(ALookback: Int64);
|
||||||
|
function GetData(var Data: TMycDataArray<T>): Boolean;
|
||||||
|
property Data: TWriteable<TMycDataArray<T>> read FData;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TChartSeriesProcessor<T> = class(TMycChart.TSeries)
|
||||||
strict private
|
strict private
|
||||||
FDataSeries: TMycDataArray<T>;
|
FDataSeries: TMycDataArray<T>;
|
||||||
FChanged: Boolean;
|
|
||||||
FLock: TSpinLock;
|
FLock: TSpinLock;
|
||||||
function ProcessData(const Values: TArray<T>): Boolean;
|
|
||||||
private
|
private
|
||||||
FData: TMycDataArray<T>;
|
FData: TMycDataArray<T>;
|
||||||
|
FDataProvider: IMycDataProvider<T>;
|
||||||
|
FReceiver: TChartSeriesReceiver<T>;
|
||||||
|
FReceiverTag: TTag;
|
||||||
protected
|
protected
|
||||||
function GetCount: Int64; override;
|
function GetCount: Int64; override;
|
||||||
function Update: Boolean; override;
|
function Update: Boolean; override;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TMycChart);
|
constructor Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider<T>);
|
||||||
|
destructor Destroy; override;
|
||||||
property Data: TMycDataArray<T> read FData;
|
property Data: TMycDataArray<T> read FData;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -100,7 +123,12 @@ type
|
|||||||
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; override;
|
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; override;
|
||||||
procedure Paint(const ACanvas: TCanvas; const AXForm, AYForm: TFunc<Double, Single>); override;
|
procedure Paint(const ACanvas: TCanvas; const AXForm, AYForm: TFunc<Double, Single>); override;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TMycChart; const AUpColor, ADownColor: TAlphaColor; AStyle: TCandleStyle);
|
constructor Create(
|
||||||
|
AOwner: TMycChart;
|
||||||
|
const ADataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||||
|
const AUpColor, ADownColor: TAlphaColor;
|
||||||
|
AStyle: TCandleStyle
|
||||||
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TChartLineSeries }
|
{ TChartLineSeries }
|
||||||
@@ -112,14 +140,20 @@ type
|
|||||||
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; override;
|
function GetValueRange(StartIndex, Count: Int64; out Min, Max: Double): Boolean; override;
|
||||||
procedure Paint(const ACanvas: TCanvas; const AXForm, AYForm: TFunc<Double, Single>); override;
|
procedure Paint(const ACanvas: TCanvas; const AXForm, AYForm: TFunc<Double, Single>); override;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TMycChart; const ALineColor: TAlphaColor; ALineWidth: Single);
|
constructor Create(
|
||||||
|
AOwner: TMycChart;
|
||||||
|
const ADataProvider: IMycDataProvider<Double>;
|
||||||
|
const ALineColor: TAlphaColor;
|
||||||
|
ALineWidth: Single
|
||||||
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TMycChart.TSeries }
|
{ TMycChart.TSeries }
|
||||||
|
|
||||||
constructor TMycChart.TSeries.Create(AOwner: TMycChart);
|
constructor TMycChart.TSeries.Create(AOwner: TMycChart);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create;
|
||||||
|
FOwner := AOwner;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycChart.TSeries.GetMainSeries: TSeries;
|
function TMycChart.TSeries.GetMainSeries: TSeries;
|
||||||
@@ -129,18 +163,13 @@ begin
|
|||||||
Result := Owner.FSeriesList[0];
|
Result := Owner.FSeriesList[0];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycChart.TSeries.GetOwner: TMycChart;
|
|
||||||
begin
|
|
||||||
Result := Controller as TMycChart;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TMycChart }
|
{ TMycChart }
|
||||||
|
|
||||||
constructor TMycChart.Create(AOwner: TComponent);
|
constructor TMycChart.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
FSeriesList := TObjectList<TSeries>.Create(true);
|
FSeriesList := TObjectList<TSeries>.Create(true);
|
||||||
FLookback := 100; // Default lookback
|
FLookback := 10000;
|
||||||
|
|
||||||
FIdleSubscrId :=
|
FIdleSubscrId :=
|
||||||
TMessageManager
|
TMessageManager
|
||||||
@@ -156,27 +185,29 @@ begin
|
|||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycChart.CreateDoubleListener(const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue; const ALineWidth: Single = 1.5):
|
procedure TMycChart.AddDoubleSeries(
|
||||||
IMycProcessor<TArray<Double>>;
|
const DataProvider: IMycDataProvider<Double>;
|
||||||
|
const ALineColor: TAlphaColor = TAlphaColors.Cornflowerblue;
|
||||||
|
const ALineWidth: Single = 1.5
|
||||||
|
);
|
||||||
var
|
var
|
||||||
series: TChartLineSeries;
|
series: TChartLineSeries;
|
||||||
begin
|
begin
|
||||||
series := TChartLineSeries.Create(Self, ALineColor, ALineWidth);
|
series := TChartLineSeries.Create(Self, DataProvider, ALineColor, ALineWidth);
|
||||||
FSeriesList.Add(series);
|
FSeriesList.Add(series);
|
||||||
Result := series;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycChart.CreateOhlcListener(
|
procedure TMycChart.AddOhlcSeries(
|
||||||
|
const DataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||||
const AUpColor: TAlphaColor = TAlphaColors.Green;
|
const AUpColor: TAlphaColor = TAlphaColors.Green;
|
||||||
const ADownColor: TAlphaColor = TAlphaColors.Red;
|
const ADownColor: TAlphaColor = TAlphaColors.Red;
|
||||||
const AStyle: TCandleStyle = csCandleStick
|
const AStyle: TCandleStyle = csCandleStick
|
||||||
): IMycProcessor<TArray<TDataPoint<TOhlcItem>>>;
|
);
|
||||||
var
|
var
|
||||||
series: TChartOhlcSeries;
|
series: TChartOhlcSeries;
|
||||||
begin
|
begin
|
||||||
series := TChartOhlcSeries.Create(Self, AUpColor, ADownColor, AStyle);
|
series := TChartOhlcSeries.Create(Self, DataProvider, AUpColor, ADownColor, AStyle);
|
||||||
FSeriesList.Add(series);
|
FSeriesList.Add(series);
|
||||||
Result := series;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycChart.DoIdle;
|
procedure TMycChart.DoIdle;
|
||||||
@@ -241,14 +272,11 @@ begin
|
|||||||
xTransform := function(index: Double): Single begin Result := rect.Right - (index / (FLookback - 1)) * rect.Width; end;
|
xTransform := function(index: Double): Single begin Result := rect.Right - (index / (FLookback - 1)) * rect.Width; end;
|
||||||
|
|
||||||
yTransform :=
|
yTransform :=
|
||||||
function(value: Double): Single
|
function(value: Double): Single begin Result := rect.Top + (1 - (value - globalMin) / (globalMax - globalMin)) * rect.Height; end;
|
||||||
begin
|
|
||||||
Result := rect.Top + (1 - (value - globalMin) / (globalMax - globalMin)) * rect.Height;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// var T :=
|
// var T :=
|
||||||
// TMatrix.CreateTranslation(rect.Left, rect.Top + rect.Height*globalMax / (globalMax - globalMin)) *
|
// TMatrix.CreateTranslation(rect.Left, rect.Top + rect.Height*globalMax / (globalMax - globalMin)) *
|
||||||
// TMatrix.CreateScaling(rect.Width / (FLookback - 1), -rect.Height / (globalMax - globalMin));
|
// TMatrix.CreateScaling(rect.Width / (FLookback - 1), -rect.Height / (globalMax - globalMin));
|
||||||
|
|
||||||
for series in FSeriesList do
|
for series in FSeriesList do
|
||||||
begin
|
begin
|
||||||
@@ -258,12 +286,21 @@ end;
|
|||||||
|
|
||||||
{ TChartSeriesProcessor<T> }
|
{ TChartSeriesProcessor<T> }
|
||||||
|
|
||||||
constructor TChartSeriesProcessor<T>.Create(AOwner: TMycChart);
|
constructor TChartSeriesProcessor<T>.Create(AOwner: TMycChart; const ADataProvider: IMycDataProvider<T>);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
FLock := TSpinLock.Create(false);
|
FLock := TSpinLock.Create(false);
|
||||||
FDataSeries := TMycDataArray<T>.CreateEmpty;
|
FDataSeries := TMycDataArray<T>.CreateEmpty;
|
||||||
FData := FDataSeries;
|
FData := FDataSeries;
|
||||||
|
FDataProvider := ADataProvider;
|
||||||
|
FReceiver := TChartSeriesReceiver<T>.Create(AOwner.Lookback);
|
||||||
|
FReceiverTag := FDataProvider.Link(FReceiver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TChartSeriesProcessor<T>.Destroy;
|
||||||
|
begin
|
||||||
|
FDataProvider.Unlink(FReceiverTag);
|
||||||
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChartSeriesProcessor<T>.GetCount: Int64;
|
function TChartSeriesProcessor<T>.GetCount: Int64;
|
||||||
@@ -271,36 +308,21 @@ begin
|
|||||||
Result := FData.Count;
|
Result := FData.Count;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChartSeriesProcessor<T>.ProcessData(const Values: TArray<T>): Boolean;
|
|
||||||
begin
|
|
||||||
Result := true;
|
|
||||||
FLock.Enter;
|
|
||||||
try
|
|
||||||
FDataSeries := FDataSeries.Add(Values, 0, Length(Values), Owner.Lookback);
|
|
||||||
FChanged := true;
|
|
||||||
finally
|
|
||||||
FLock.Exit;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TChartSeriesProcessor<T>.Update: Boolean;
|
function TChartSeriesProcessor<T>.Update: Boolean;
|
||||||
begin
|
begin
|
||||||
FLock.Enter;
|
Result := FReceiver.GetData(FData);
|
||||||
try
|
|
||||||
Result := FChanged;
|
|
||||||
FChanged := false;
|
|
||||||
if Result then
|
|
||||||
FData := FDataSeries;
|
|
||||||
finally
|
|
||||||
FLock.Exit;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TChartOhlcSeries }
|
{ TChartOhlcSeries }
|
||||||
|
|
||||||
constructor TChartOhlcSeries.Create(AOwner: TMycChart; const AUpColor, ADownColor: TAlphaColor; AStyle: TCandleStyle);
|
constructor TChartOhlcSeries.Create(
|
||||||
|
AOwner: TMycChart;
|
||||||
|
const ADataProvider: IMycDataProvider<TDataPoint<TOhlcItem>>;
|
||||||
|
const AUpColor, ADownColor: TAlphaColor;
|
||||||
|
AStyle: TCandleStyle
|
||||||
|
);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner, ADataProvider);
|
||||||
FUpColor := AUpColor;
|
FUpColor := AUpColor;
|
||||||
FDownColor := ADownColor;
|
FDownColor := ADownColor;
|
||||||
FStyle := AStyle;
|
FStyle := AStyle;
|
||||||
@@ -318,8 +340,9 @@ begin
|
|||||||
Max := -MaxDouble;
|
Max := -MaxDouble;
|
||||||
for i := StartIndex to System.Math.Min(GetCount - 1, StartIndex + Count - 1) do
|
for i := StartIndex to System.Math.Min(GetCount - 1, StartIndex + Count - 1) do
|
||||||
begin
|
begin
|
||||||
Min := System.Math.Min(Min, Data.Items[i].Data.Low);
|
var dp := Data.Items[i];
|
||||||
Max := System.Math.Max(Max, Data.Items[i].Data.High);
|
Min := System.Math.Min(Min, dp.Data.Low);
|
||||||
|
Max := System.Math.Max(Max, dp.Data.High);
|
||||||
end;
|
end;
|
||||||
Result := (Min <> MaxDouble);
|
Result := (Min <> MaxDouble);
|
||||||
end;
|
end;
|
||||||
@@ -372,9 +395,14 @@ end;
|
|||||||
|
|
||||||
{ TChartLineSeries }
|
{ TChartLineSeries }
|
||||||
|
|
||||||
constructor TChartLineSeries.Create(AOwner: TMycChart; const ALineColor: TAlphaColor; ALineWidth: Single);
|
constructor TChartLineSeries.Create(
|
||||||
|
AOwner: TMycChart;
|
||||||
|
const ADataProvider: IMycDataProvider<Double>;
|
||||||
|
const ALineColor: TAlphaColor;
|
||||||
|
ALineWidth: Single
|
||||||
|
);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner, ADataProvider);
|
||||||
FLineColor := ALineColor;
|
FLineColor := ALineColor;
|
||||||
FLineWidth := ALineWidth;
|
FLineWidth := ALineWidth;
|
||||||
end;
|
end;
|
||||||
@@ -389,10 +417,14 @@ begin
|
|||||||
|
|
||||||
Min := MaxDouble;
|
Min := MaxDouble;
|
||||||
Max := -MaxDouble;
|
Max := -MaxDouble;
|
||||||
for i := StartIndex to System.Math.Min(GetCount - 1, StartIndex + Count - 1) do
|
for i := StartIndex to System.Math.Min(Data.Count - 1, StartIndex + Count - 1) do
|
||||||
begin
|
begin
|
||||||
Min := System.Math.Min(Min, Data.Items[i]);
|
var v := Data.Items[i];
|
||||||
Max := System.Math.Max(Max, Data.Items[i]);
|
if not IsNaN(v) then
|
||||||
|
begin
|
||||||
|
Min := System.Math.Min(Min, v);
|
||||||
|
Max := System.Math.Max(Max, v);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
Result := (Min <> MaxDouble);
|
Result := (Min <> MaxDouble);
|
||||||
end;
|
end;
|
||||||
@@ -412,18 +444,46 @@ begin
|
|||||||
var n := 0;
|
var n := 0;
|
||||||
while IsNaN(Data[n]) do
|
while IsNaN(Data[n]) do
|
||||||
begin
|
begin
|
||||||
inc( n );
|
inc(n);
|
||||||
if n >= displaycount-2 then
|
if n >= displaycount - 2 then
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
points.MoveTo( TPointF.Create(AXForm(n), AYForm(Data[n])) );
|
points.MoveTo(TPointF.Create(AXForm(n), AYForm(Data[n])));
|
||||||
for var i := n to displayCount - 1 do
|
for var i := n to displayCount - 1 do
|
||||||
points.LineTo( TPointF.Create(AXForm(i), AYForm(Data[i])) );
|
points.LineTo(TPointF.Create(AXForm(i), AYForm(Data[i])));
|
||||||
|
|
||||||
ACanvas.Stroke.Color := FLineColor;
|
ACanvas.Stroke.Color := FLineColor;
|
||||||
ACanvas.Stroke.Thickness := FLineWidth;
|
ACanvas.Stroke.Thickness := FLineWidth;
|
||||||
ACanvas.DrawPath(points, 1);
|
ACanvas.DrawPath(points, 1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TChartSeriesReceiver<T> }
|
||||||
|
|
||||||
|
constructor TChartSeriesReceiver<T>.Create(ALookback: Int64);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FLookback := ALookback;
|
||||||
|
FCurrData := TMycDataArray<T>.CreateEmpty;
|
||||||
|
FData := TWriteable<TMycDataArray<T>>.CreateWriteable( FCurrData ).Protect;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TChartSeriesReceiver<T>.GetData(var Data: TMycDataArray<T>): Boolean;
|
||||||
|
begin
|
||||||
|
var prevTotalCount := Data.TotalCount;
|
||||||
|
Data := FData.Value;
|
||||||
|
Result := prevTotalCount <> Data.TotalCount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TChartSeriesReceiver<T>.ProcessData(const Value: T): Boolean;
|
||||||
|
begin
|
||||||
|
Result := true;
|
||||||
|
FCurrData := FCurrData.Add(Value, FLookback);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartSeriesReceiver<T>.Update;
|
||||||
|
begin
|
||||||
|
FData.Value := FCurrData;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -15,17 +15,19 @@ type
|
|||||||
private
|
private
|
||||||
FChunks: TArray<TChunk>;
|
FChunks: TArray<TChunk>;
|
||||||
FCount: Int64;
|
FCount: Int64;
|
||||||
|
FTotalCount: Int64;
|
||||||
|
|
||||||
function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline;
|
function LogicalToPhysicalIndex(LogicalIndex: Int64): Int64; inline;
|
||||||
function GetItems(Idx: Int64): T; inline;
|
function GetItems(Idx: Int64): T; inline;
|
||||||
public
|
public
|
||||||
constructor Create(const AChunks: TArray<TChunk>; ACount: Int64);
|
constructor Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||||
function Add(const Data: T; Lookback: Int64): TMycDataArray<T>; overload;
|
function Add(const Data: T; Lookback: Int64): TMycDataArray<T>; overload;
|
||||||
function Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray<T>; overload;
|
function Add(const Data: array of T; First, Count, Lookback: Int64): TMycDataArray<T>; overload;
|
||||||
class function CreateEmpty: TMycDataArray<T>; static;
|
class function CreateEmpty: TMycDataArray<T>; static;
|
||||||
// Helper to create a data array from a raw TArray.
|
// Helper to create a data array from a raw TArray.
|
||||||
class function CreateFromArray(const AData: TArray<T>; First, Count: Integer): TMycDataArray<T>; static;
|
class function CreateFromArray(const AData: TArray<T>; First, Count: Integer): TMycDataArray<T>; static;
|
||||||
property Count: Int64 read FCount;
|
property Count: Int64 read FCount;
|
||||||
|
property TotalCount: Int64 read FTotalCount;
|
||||||
property Items[Idx: Int64]: T read GetItems; default;
|
property Items[Idx: Int64]: T read GetItems; default;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -33,10 +35,11 @@ implementation
|
|||||||
|
|
||||||
{ TMycDataArray<T> }
|
{ TMycDataArray<T> }
|
||||||
|
|
||||||
constructor TMycDataArray<T>.Create(const AChunks: TArray<TChunk>; ACount: Int64);
|
constructor TMycDataArray<T>.Create(const AChunks: TArray<TChunk>; ACount, ATotalCount: Int64);
|
||||||
begin
|
begin
|
||||||
FChunks := AChunks;
|
FChunks := AChunks;
|
||||||
FCount := ACount;
|
FCount := ACount;
|
||||||
|
FTotalCount := ATotalCount;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TMycDataArray<T>.CreateEmpty: TMycDataArray<T>;
|
class function TMycDataArray<T>.CreateEmpty: TMycDataArray<T>;
|
||||||
@@ -98,7 +101,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := TMycDataArray<T>.Create(newChunks, newCount);
|
Result := TMycDataArray<T>.Create(newChunks, newCount, FTotalCount + Count);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycDataArray<T>.Add(const Data: T; Lookback: Int64): TMycDataArray<T>;
|
function TMycDataArray<T>.Add(const Data: T; Lookback: Int64): TMycDataArray<T>;
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ unit Myc.Trade.DataPoint;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.TimeSpan;
|
System.TimeSpan,
|
||||||
|
Myc.Core.Notifier;
|
||||||
|
|
||||||
type
|
type
|
||||||
// A data record for an Ask/Bid price pair.
|
// A data record for an Ask/Bid price pair.
|
||||||
@@ -31,11 +32,34 @@ type
|
|||||||
|
|
||||||
IMycProcessor<T> = interface
|
IMycProcessor<T> = interface
|
||||||
function ProcessData(const Value: T): Boolean;
|
function ProcessData(const Value: T): Boolean;
|
||||||
|
procedure Update;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
||||||
protected
|
protected
|
||||||
function ProcessData(const Value: T): Boolean; virtual; abstract;
|
function ProcessData(const Value: T): Boolean; virtual; abstract;
|
||||||
|
procedure Update; virtual;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TTag = Pointer;
|
||||||
|
|
||||||
|
IMycDataProvider<T> = interface
|
||||||
|
function Link(const Receiver: IMycProcessor<T>): TTag;
|
||||||
|
procedure Unlink(Tag: TTag);
|
||||||
|
end;
|
||||||
|
|
||||||
|
TMycDataProvider<T> = class abstract(TContainedObject, IMycDataProvider<T>)
|
||||||
|
private
|
||||||
|
FListeners: TMycNotifyList<IMycProcessor<T>>;
|
||||||
|
public
|
||||||
|
constructor Create(const Controller: IInterface);
|
||||||
|
destructor Destroy; override;
|
||||||
|
// Notifies all linked processors.
|
||||||
|
procedure Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
|
||||||
|
// Link a Processor
|
||||||
|
function Link(const Processor: IMycProcessor<T>): TTag;
|
||||||
|
// Unlink a linked strategy
|
||||||
|
procedure Unlink(Tag: TTag);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
||||||
@@ -43,6 +67,7 @@ type
|
|||||||
TProc = reference to function(const Value: T): Boolean;
|
TProc = reference to function(const Value: T): Boolean;
|
||||||
private
|
private
|
||||||
FProc: TProc;
|
FProc: TProc;
|
||||||
|
protected
|
||||||
function ProcessData(const Value: T): Boolean; override;
|
function ProcessData(const Value: T): Boolean; override;
|
||||||
public
|
public
|
||||||
constructor Create(const AProc: TProc);
|
constructor Create(const AProc: TProc);
|
||||||
@@ -54,6 +79,7 @@ type
|
|||||||
private
|
private
|
||||||
FProc: TProc;
|
FProc: TProc;
|
||||||
function ProcessData(const Value: T): Boolean;
|
function ProcessData(const Value: T): Boolean;
|
||||||
|
procedure Update;
|
||||||
public
|
public
|
||||||
constructor Create(const Controller: IInterface; const AProc: TProc);
|
constructor Create(const Controller: IInterface; const AProc: TProc);
|
||||||
end;
|
end;
|
||||||
@@ -441,4 +467,58 @@ begin
|
|||||||
Result := FProc(Value);
|
Result := FProc(Value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TMycContainedProcessor<T>.Update;
|
||||||
|
begin
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TMycDataProvider<S, T> }
|
||||||
|
|
||||||
|
constructor TMycDataProvider<T>.Create(const Controller: IInterface);
|
||||||
|
begin
|
||||||
|
inherited Create(Controller);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TMycDataProvider<T>.Destroy;
|
||||||
|
begin
|
||||||
|
FListeners.Finalize;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMycDataProvider<T>.Notify(const Func: TMycNotifyList<IMycProcessor<T>>.TNotifyProc);
|
||||||
|
begin
|
||||||
|
FListeners.Lock;
|
||||||
|
try
|
||||||
|
FListeners.Notify(Func);
|
||||||
|
finally
|
||||||
|
FListeners.Release;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycDataProvider<T>.Link(const Processor: IMycProcessor<T>): TTag;
|
||||||
|
begin
|
||||||
|
// Add the Processor to the notification list
|
||||||
|
FListeners.Lock;
|
||||||
|
try
|
||||||
|
Result := FListeners.Advise(Processor);
|
||||||
|
finally
|
||||||
|
FListeners.Release;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMycDataProvider<T>.Unlink(Tag: TTag);
|
||||||
|
begin
|
||||||
|
FListeners.Lock;
|
||||||
|
try
|
||||||
|
FListeners.Unadvise(Tag);
|
||||||
|
finally
|
||||||
|
FListeners.Release;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMycProcessor<T>.Update;
|
||||||
|
begin
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@@ -437,7 +437,8 @@ begin
|
|||||||
function(const Data: TArray<TDataPoint<T>>): TState
|
function(const Data: TArray<TDataPoint<T>>): TState
|
||||||
begin
|
begin
|
||||||
if not Processor.ProcessData(Data) then
|
if not Processor.ProcessData(Data) then
|
||||||
exit( TState.Null );
|
exit(TState.Null);
|
||||||
|
Processor.Update;
|
||||||
|
|
||||||
Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor);
|
Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor);
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
unit Myc.Trade.Ticker;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
uses
|
|
||||||
System.SysUtils,
|
|
||||||
Myc.Signals,
|
|
||||||
Myc.Lazy,
|
|
||||||
Myc.Trade.DataPoint,
|
|
||||||
Myc.Trade.DataStream,
|
|
||||||
Myc.Trade.DataProvider;
|
|
||||||
|
|
||||||
type
|
|
||||||
TTickProc<T> = reference to procedure(const Tick: TDataPoint<T>);
|
|
||||||
|
|
||||||
TTicker<T> = class(TInterfacedObject, TSignal.ISubscriber)
|
|
||||||
type
|
|
||||||
TConsumer = record
|
|
||||||
Proc: TTickProc<T>;
|
|
||||||
Lookback: Int64;
|
|
||||||
end;
|
|
||||||
private
|
|
||||||
FStream: IDataStream<T>;
|
|
||||||
FConsumers: TArray<TConsumer>;
|
|
||||||
FProvider: TMutable<TDataSeries<T>>;
|
|
||||||
|
|
||||||
function Notify: Boolean;
|
|
||||||
public
|
|
||||||
constructor Create(const AStream: IDataStream<T>; const AConsumers: TArray<TConsumer>);
|
|
||||||
|
|
||||||
end;
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
constructor TTicker<T>.Create(const AStream: IDataStream<T>; const AConsumers: TArray<TConsumer>);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
FStream := AStream;
|
|
||||||
FConsumers := AConsumers;
|
|
||||||
|
|
||||||
var ml: Int64 := 0;
|
|
||||||
for var consumer in FConsumers do
|
|
||||||
if consumer.Lookback > ml then
|
|
||||||
ml := consumer.Lookback;
|
|
||||||
|
|
||||||
FProvider := TDataStreamProvider<T>.Create(ml, 100, AStream);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TTicker<T> }
|
|
||||||
|
|
||||||
function TTicker<T>.Notify: Boolean;
|
|
||||||
begin
|
|
||||||
|
|
||||||
end;
|
|
||||||
|
|
||||||
end.
|
|
||||||
Reference in New Issue
Block a user