DataFlow refactoring
This commit is contained in:
+37
-253
@@ -111,7 +111,6 @@ type
|
||||
procedure NewWorkspace;
|
||||
function CurrLayout<T: TControl>: T;
|
||||
procedure AlignControl(Control: TControl);
|
||||
function CreateStrategy1(Timeframe: TTimeframe): IMycProcessor<TDataPoint<TOhlcItem>>;
|
||||
function CreateStrategy2(Timeframe: TTimeframe): IMycProcessor<TDataPoint<TOhlcItem>>;
|
||||
|
||||
published
|
||||
@@ -286,221 +285,6 @@ begin
|
||||
Control.Align := TAlignLayout.Top;
|
||||
end;
|
||||
|
||||
function TForm1.CreateStrategy1(Timeframe: TTimeframe): IMycProcessor<TDataPoint<TOhlcItem>>;
|
||||
type
|
||||
TSignal = record
|
||||
Sig: Double;
|
||||
SL: Double;
|
||||
Entry: Double;
|
||||
pnl: Double;
|
||||
end;
|
||||
var
|
||||
panel: TMycChart.TPanel;
|
||||
begin
|
||||
var ticker := TConverter.CreateIdentity<TDataPoint<TOhlcItem>>;
|
||||
Result := ticker;
|
||||
|
||||
var OhlcPoint := ticker.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateOhlcAggregation(Timeframe));
|
||||
|
||||
var Ohlc := TConverter.CreateSequence<TOhlcItem>(2, OhlcPoint.Field<TOhlcItem>('Data').Sender);
|
||||
|
||||
var Closes := Ohlc[0].Field<Double>('Close');
|
||||
|
||||
var Hull := Closes.Chain<Double>(TIndicators.CreateHMA(250)).MakeParallel;
|
||||
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(200)).MakeParallel;
|
||||
|
||||
var Lowest: Double := Double.MaxValue;
|
||||
var Highest: Double := Double.MinValue;
|
||||
|
||||
var ATR := Ohlc[0].Chain<Double>(TIndicators.CreateATR(50)).MakeParallel;
|
||||
|
||||
// next stage
|
||||
|
||||
var ATREndPoint := TConverter.CreateEndpoint<Double>(ATR.Sender, 5);
|
||||
var ATRSeries: TSeries<Double>;
|
||||
|
||||
var HullEndPoint := TConverter.CreateEndpoint<Double>(Hull.Sender, 5);
|
||||
var HullSeries: TSeries<Double>;
|
||||
|
||||
var SmaEndPoint := TConverter.CreateEndpoint<Double>(Sma.Sender, 5);
|
||||
var SmaSeries: TSeries<Double>;
|
||||
|
||||
var curr: TSignal;
|
||||
curr.SL := Double.NaN;
|
||||
curr.Entry := Double.NaN;
|
||||
|
||||
var Signal :=
|
||||
Ohlc[1]
|
||||
.Chain<TSignal>(
|
||||
function(const Ohlc: TOhlcItem): TSignal
|
||||
begin
|
||||
if Ohlc.Low < Lowest then
|
||||
Lowest := Ohlc.Low;
|
||||
if Ohlc.High > Highest then
|
||||
Highest := Ohlc.High;
|
||||
|
||||
Result := curr;
|
||||
Result.Sig := 0;
|
||||
var pnl: double := NaN;
|
||||
|
||||
ATREndPoint.Update(ATRSeries);
|
||||
HullEndPoint.Update(HullSeries);
|
||||
SmaEndPoint.Update(SmaSeries);
|
||||
|
||||
if (HullSeries[0] < SmaSeries[0]) and (HullSeries[1] >= SmaSeries[1]) then
|
||||
begin
|
||||
if curr.Sig > 0 then
|
||||
pnl := Ohlc.Close - curr.Entry;
|
||||
|
||||
curr.Sig := -1;
|
||||
curr.SL := Highest;
|
||||
curr.Entry := Ohlc.Close;
|
||||
Result := curr;
|
||||
end
|
||||
else if (HullSeries[0] > SmaSeries[0]) and (HullSeries[1] <= SmaSeries[1]) then
|
||||
begin
|
||||
if curr.Sig < 0 then
|
||||
pnl := curr.Entry - Ohlc.Close;
|
||||
|
||||
curr.Sig := 1;
|
||||
curr.SL := Lowest;
|
||||
curr.Entry := Ohlc.Close;
|
||||
Result := curr;
|
||||
end;
|
||||
|
||||
var atr := 15 * ATRSeries[0];
|
||||
if curr.Sig > 0 then
|
||||
begin
|
||||
if Ohlc.Close > curr.SL then
|
||||
begin
|
||||
if curr.SL < Ohlc.Close - atr then
|
||||
curr.SL := Ohlc.Close - atr;
|
||||
Result.SL := curr.SL;
|
||||
end;
|
||||
|
||||
if Ohlc.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 Ohlc.Close < curr.SL then
|
||||
begin
|
||||
if curr.SL > Ohlc.Close + atr then
|
||||
curr.SL := Ohlc.Close + atr;
|
||||
Result.SL := curr.SL;
|
||||
end;
|
||||
|
||||
if Ohlc.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;
|
||||
end);
|
||||
|
||||
var pnl := Signal.Field<Double>('pnl');
|
||||
|
||||
var FEquity: Double;
|
||||
var FInit: Boolean;
|
||||
|
||||
var equity :=
|
||||
TConverter<Double, Double>.CreateAggregation(
|
||||
function(const Value: Double; const Broadcast: TConverter<Double, Double>.TBroadcastProc): 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
|
||||
);
|
||||
|
||||
// var equity: TConverter<Double, Double> := TEquitySum.Create(10000);
|
||||
pnl.Sender.Link(equity);
|
||||
|
||||
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(M15, OhlcPoint.Field<TDateTime>('Time').Sender);
|
||||
|
||||
panel := chart.AddPanel;
|
||||
panel.AddOhlcSeries(Ohlc[0].Sender);
|
||||
panel.AddDoubleSeries(Hull.Sender, TAlphaColors.Cornflowerblue, 2);
|
||||
panel.AddDoubleSeries(Sma.Sender, TAlphaColors.Brown, 1.5);
|
||||
panel.AddDoubleSeries(Signal.Field<Double>('Entry').Sender, TAlphaColors.Green, 1);
|
||||
panel.AddDoubleSeries(Signal.Field<Double>('SL').Sender, TAlphaColors.Red, 2);
|
||||
|
||||
var mean := TConverter<TArray<Double>, Double>.CreateGeneric(TIndicators.CreateMean());
|
||||
|
||||
TConverter.Join<Double>([Hull.Sender, Sma.Sender]).Link(mean);
|
||||
|
||||
panel.AddDoubleSeries(mean.Sender, TAlphaColors.Blue, 5);
|
||||
|
||||
var pnlChart := TMycChart.Create(Self);
|
||||
AlignControl(pnlChart);
|
||||
pnlChart.Height := Layout.ChildrenRect.Width * 9 / 24;
|
||||
pnlChart.Lookback.Value := 50000;
|
||||
|
||||
pnlChart.SetXAxisCounter<Double>(equity.Sender);
|
||||
|
||||
////////////
|
||||
var EMAFactory := TEMA.Create;
|
||||
|
||||
var Params := TDataRecord.Create(EMAFactory.Params);
|
||||
Params.SetValue<Integer>('Period', 20);
|
||||
|
||||
var indi := EMAFActory.CreateIndicator(Params);
|
||||
|
||||
var EMAConv := TConverter<TDataRecord, TDataRecord>.CreateGeneric(indi);
|
||||
|
||||
var equityEMA :=
|
||||
equity
|
||||
.Chain<TDataRecord>(TConverter.FieldToRecord<Double>(EMAFactory.Input, 'Price'))
|
||||
.Chain<TDataRecord>(EMAConv)
|
||||
.Chain<Double>(TConverter.FieldOfRecord<Double>(EMAFactory.Output, 'MA'));
|
||||
|
||||
//////////////
|
||||
|
||||
panel := pnlChart.AddPanel;
|
||||
panel.AddDoubleSeries(equity.Sender, TAlphaColors.Blue, 3);
|
||||
panel.AddDoubleSeries(equityEMA.Sender, TAlphaColors.Gray, 2);
|
||||
|
||||
/////
|
||||
end;
|
||||
|
||||
function TForm1.CreateStrategy2(Timeframe: TTimeframe): IMycProcessor<TDataPoint<TOhlcItem>>;
|
||||
type
|
||||
TSignal = record
|
||||
@@ -515,7 +299,7 @@ begin
|
||||
var ticker := TConverter.CreateIdentity<TDataPoint<TOhlcItem>>;
|
||||
Result := ticker;
|
||||
|
||||
var OhlcPoint := ticker.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateOhlcAggregation(Timeframe));
|
||||
var OhlcPoint := ticker.Sender.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateOhlcAggregation(Timeframe));
|
||||
|
||||
var Ohlc := OhlcPoint.Field<TOhlcItem>('Data');
|
||||
|
||||
@@ -531,13 +315,13 @@ begin
|
||||
|
||||
// next stage
|
||||
|
||||
var ATREndPoint := TConverter.CreateEndpoint<Double>(ATR.Sender, 5);
|
||||
var ATREndPoint := ATR.CreateEndpoint(5);
|
||||
var ATRSeries: TSeries<Double>;
|
||||
|
||||
var HullEndPoint := TConverter.CreateEndpoint<Double>(Hull.Sender, 5);
|
||||
var HullEndPoint := Hull.CreateEndpoint(5);
|
||||
var HullSeries: TSeries<Double>;
|
||||
|
||||
var SmaEndPoint := TConverter.CreateEndpoint<Double>(Sma.Sender, 5);
|
||||
var SmaEndPoint := Sma.CreateEndpoint(5);
|
||||
var SmaSeries: TSeries<Double>;
|
||||
|
||||
var curr: TSignal;
|
||||
@@ -546,10 +330,7 @@ begin
|
||||
|
||||
var lastHull, lastSma: Double;
|
||||
|
||||
var conv :=
|
||||
TConverter.Join<Double>(
|
||||
[Ohlc.Field<Double>('Low').Sender, Ohlc.Field<Double>('High').Sender, Closes.Sender, ATR.Sender, Hull.Sender, Sma.Sender]
|
||||
);
|
||||
var conv := TConverter.Join<Double>([Ohlc.Field<Double>('Low'), Ohlc.Field<Double>('High'), Closes, ATR, Hull, Sma]);
|
||||
|
||||
var Signal :=
|
||||
TConverter<TArray<Double>, TSignal>.CreateGeneric(
|
||||
@@ -643,9 +424,9 @@ begin
|
||||
end
|
||||
);
|
||||
|
||||
conv.Link(Signal);
|
||||
conv.Chain<TSignal>(Signal);
|
||||
|
||||
var pnl := Signal.Field<Double>('pnl');
|
||||
var pnl := Signal.Sender.Field<Double>('pnl');
|
||||
|
||||
var FEquity: Double := 10000;
|
||||
var FInit: Boolean := false;
|
||||
@@ -668,7 +449,7 @@ begin
|
||||
end
|
||||
);
|
||||
|
||||
pnl.Sender.Link(equity);
|
||||
pnl.Chain<Double>(equity);
|
||||
|
||||
var Layout := CurrLayout<TVertScrollBox>;
|
||||
if Layout = nil then
|
||||
@@ -683,18 +464,18 @@ begin
|
||||
chart.Height := Layout.ChildrenRect.Width * 9 / 16;
|
||||
chart.Lookback.Value := 50000;
|
||||
|
||||
chart.SetXAxisSeries(M15, OhlcPoint.Field<TDateTime>('Time').Sender);
|
||||
chart.SetXAxisSeries(M15, OhlcPoint.Field<TDateTime>('Time'));
|
||||
|
||||
panel := chart.AddPanel;
|
||||
panel.AddOhlcSeries(Ohlc.Sender);
|
||||
panel.AddDoubleSeries(Hull.Sender, TAlphaColors.Cornflowerblue, 2);
|
||||
panel.AddDoubleSeries(Sma.Sender, TAlphaColors.Brown, 1.5);
|
||||
panel.AddDoubleSeries(Signal.Field<Double>('Entry').Sender, TAlphaColors.Green, 1);
|
||||
panel.AddDoubleSeries(Signal.Field<Double>('SL').Sender, TAlphaColors.Red, 2);
|
||||
panel.AddOhlcSeries(Ohlc);
|
||||
panel.AddDoubleSeries(Hull, TAlphaColors.Cornflowerblue, 2);
|
||||
panel.AddDoubleSeries(Sma, TAlphaColors.Brown, 1.5);
|
||||
panel.AddDoubleSeries(Signal.Sender.Field<Double>('Entry'), TAlphaColors.Green, 1);
|
||||
panel.AddDoubleSeries(Signal.Sender.Field<Double>('SL'), TAlphaColors.Red, 2);
|
||||
|
||||
var mean := TConverter<TArray<Double>, Double>.CreateGeneric(TIndicators.CreateMean());
|
||||
|
||||
TConverter.Join<Double>([Hull.Sender, Sma.Sender]).Link(mean);
|
||||
TConverter.Join<Double>([Hull, Sma]).Chain<Double>(mean);
|
||||
|
||||
panel.AddDoubleSeries(mean.Sender, TAlphaColors.Blue, 5);
|
||||
|
||||
@@ -717,6 +498,7 @@ begin
|
||||
|
||||
var equityEMA :=
|
||||
equity
|
||||
.Sender
|
||||
.Chain<TDataRecord>(TConverter.FieldToRecord<Double>(EMAFactory.Input, 'Price'))
|
||||
.Chain<TDataRecord>(EMAConv)
|
||||
.Chain<Double>(TConverter.FieldOfRecord<Double>(EMAFactory.Output, 'MA'));
|
||||
@@ -725,7 +507,7 @@ begin
|
||||
|
||||
panel := pnlChart.AddPanel;
|
||||
panel.AddDoubleSeries(equity.Sender, TAlphaColors.Blue, 3);
|
||||
panel.AddDoubleSeries(equityEMA.Sender, TAlphaColors.Gray, 2);
|
||||
panel.AddDoubleSeries(equityEMA, TAlphaColors.Gray, 2);
|
||||
|
||||
/////
|
||||
end;
|
||||
@@ -791,7 +573,7 @@ begin
|
||||
|
||||
var ticker := TConverter.CreateTicker<TDataPoint<TOhlcItem>>;
|
||||
|
||||
ticker.Sender.Link(Processor);
|
||||
ticker.Sender.Chain(Processor);
|
||||
|
||||
FProcessDone := FProcessDone + FServer.ProcessData(Symbol, terminated, ticker);
|
||||
{$endif}
|
||||
@@ -823,7 +605,9 @@ begin
|
||||
|
||||
/////
|
||||
|
||||
var OhlcPoint := TConverter.CreateIdentity<TDataPoint<TOhlcItem>>;
|
||||
var OhlcTicker := TConverter.CreateIdentity<TDataPoint<TOhlcItem>>;
|
||||
|
||||
var OhlcPoint := OhlcTicker.Sender;
|
||||
|
||||
var timeframe := TTimeframe.H;
|
||||
|
||||
@@ -839,28 +623,28 @@ begin
|
||||
var Macd := Closes.MakeParallel.Chain<TMacdResult>(TIndicators.CreateMACD(12, 26, 9));
|
||||
var Stoch := Ohlc.MakeParallel.Chain<TStochasticResult>(TIndicators.CreateStochastic(14, 3));
|
||||
|
||||
chart.SetXAxisSeries(timeframe, Timestamps.Sender);
|
||||
chart.SetXAxisSeries(timeframe, Timestamps);
|
||||
|
||||
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.AddOhlcSeries(Ohlc);
|
||||
Panel.AddDoubleSeries(Hull, TAlphaColors.Aliceblue);
|
||||
Panel.AddDoubleSeries(Sma, TAlphaColors.Yellow);
|
||||
Panel.AddDoubleSeries(Ema, TAlphaColors.Aqua);
|
||||
Panel.AddDoubleSeries(Boli.Field<Double>('UpperBand'), TAlphaColors.Gray);
|
||||
Panel.AddDoubleSeries(Boli.Field<Double>('MiddleBand'), TAlphaColors.Darkgray, 1.0);
|
||||
Panel.AddDoubleSeries(Boli.Field<Double>('LowerBand'), TAlphaColors.Gray);
|
||||
|
||||
Panel := chart.AddPanel;
|
||||
Panel.AddDoubleSeries(Rsi.Sender, TAlphaColors.Fuchsia);
|
||||
Panel.AddDoubleSeries(Rsi, 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.AddDoubleSeries(Macd.Field<Double>('MacdLine'), TAlphaColors.Orange);
|
||||
Panel.AddDoubleSeries(Macd.Field<Double>('SignalLine'), TAlphaColors.Dodgerblue);
|
||||
Panel.AddDoubleSeries(Macd.Field<Double>('Histogram'), TAlphaColors.Lightgreen);
|
||||
|
||||
Panel := chart.AddPanel;
|
||||
Panel.AddDoubleSeries(Stoch.Field<Double>('K').Sender, TAlphaColors.Green);
|
||||
Panel.AddDoubleSeries(Stoch.Field<Double>('D').Sender, TAlphaColors.Red);
|
||||
Panel.AddDoubleSeries(Stoch.Field<Double>('K'), TAlphaColors.Green);
|
||||
Panel.AddDoubleSeries(Stoch.Field<Double>('D'), TAlphaColors.Red);
|
||||
|
||||
/////
|
||||
{
|
||||
@@ -887,7 +671,7 @@ begin
|
||||
}
|
||||
/////
|
||||
|
||||
ExecuteStrategy(Symbol, timeframe, OhlcPoint);
|
||||
ExecuteStrategy(Symbol, timeframe, OhlcTicker);
|
||||
end;
|
||||
|
||||
procedure TForm1.Strat2ButtonClick(Sender: TObject);
|
||||
@@ -897,7 +681,7 @@ begin
|
||||
exit;
|
||||
|
||||
var timeframe := TTimeframe.M15;
|
||||
// ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
|
||||
//ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
|
||||
|
||||
var tstStrat := StrategyTest.CreateStrategy1(timeframe);
|
||||
ExecuteStrategy(Symbol, timeframe, tstStrat);
|
||||
|
||||
+106
-109
@@ -10,7 +10,7 @@ uses
|
||||
Myc.DataRecord,
|
||||
Myc.Trade.Indicators;
|
||||
|
||||
function CreateStrategy1(Timeframe: TTimeframe): TConverter<TDataPoint<TOhlcItem>, Double>;
|
||||
function CreateStrategy1(Timeframe: TTimeframe): TConverter<TDataPoint<TOhlcItem>, Double>; overload;
|
||||
|
||||
implementation
|
||||
|
||||
@@ -30,7 +30,7 @@ type
|
||||
begin
|
||||
var ticker := TConverter.CreateIdentity<TDataPoint<TOhlcItem>>;
|
||||
|
||||
var OhlcPoint := ticker.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateOhlcAggregation(Timeframe));
|
||||
var OhlcPoint := ticker.Sender.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateOhlcAggregation(Timeframe));
|
||||
|
||||
var Ohlc := OhlcPoint.Field<TOhlcItem>('Data');
|
||||
|
||||
@@ -40,10 +40,7 @@ begin
|
||||
var Sma := Closes.Chain<Double>(TIndicators.CreateSMA(200)).MakeParallel;
|
||||
var ATR := Ohlc.Chain<Double>(TIndicators.CreateATR(50)).MakeParallel;
|
||||
|
||||
var conv :=
|
||||
TConverter.Join<Double>(
|
||||
[Ohlc.Field<Double>('Low').Sender, Ohlc.Field<Double>('High').Sender, Closes.Sender, ATR.Sender, Hull.Sender, Sma.Sender]
|
||||
);
|
||||
var conv := TConverter.Join<Double>([Ohlc.Field<Double>('Low'), Ohlc.Field<Double>('High'), Closes, ATR, Hull, Sma]);
|
||||
|
||||
// STAGE 1: Signal Generation. This converter is stateless regarding the trade itself.
|
||||
// It only detects the crossover event and prepares the data for the next stage.
|
||||
@@ -52,42 +49,44 @@ begin
|
||||
var lastHull, lastSma: Double;
|
||||
|
||||
var signalGenerator :=
|
||||
TConverter<TArray<Double>, TSignalEvent>.CreateGeneric(
|
||||
function(const Values: TArray<Double>): TSignalEvent
|
||||
begin
|
||||
Result.Low := Values[0];
|
||||
Result.High := Values[1];
|
||||
Result.Close := Values[2];
|
||||
Result.ATR := Values[3];
|
||||
var hull := Values[4];
|
||||
var sma := Values[5];
|
||||
|
||||
if Result.Low < Lowest then
|
||||
Lowest := Result.Low;
|
||||
if Result.High > Highest then
|
||||
Highest := Result.High;
|
||||
|
||||
Result.Signal := 0;
|
||||
Result.InitialSL := Double.NaN;
|
||||
|
||||
if (hull < sma) and (lastHull >= lastSma) then
|
||||
conv.Chain<TSignalEvent>(
|
||||
TConverter<TArray<Double>, TSignalEvent>.CreateGeneric(
|
||||
function(const Values: TArray<Double>): TSignalEvent
|
||||
begin
|
||||
Result.Signal := -1;
|
||||
Result.InitialSL := Highest;
|
||||
Highest := Double.MinValue; // Reset for next trend
|
||||
Lowest := Double.MaxValue;
|
||||
Result.Low := Values[0];
|
||||
Result.High := Values[1];
|
||||
Result.Close := Values[2];
|
||||
Result.ATR := Values[3];
|
||||
var hull := Values[4];
|
||||
var sma := Values[5];
|
||||
|
||||
if Result.Low < Lowest then
|
||||
Lowest := Result.Low;
|
||||
if Result.High > Highest then
|
||||
Highest := Result.High;
|
||||
|
||||
Result.Signal := 0;
|
||||
Result.InitialSL := Double.NaN;
|
||||
|
||||
if (hull < sma) and (lastHull >= lastSma) then
|
||||
begin
|
||||
Result.Signal := -1;
|
||||
Result.InitialSL := Highest;
|
||||
Highest := Double.MinValue; // Reset for next trend
|
||||
Lowest := Double.MaxValue;
|
||||
end
|
||||
else if (hull > sma) and (lastHull <= lastSma) then
|
||||
begin
|
||||
Result.Signal := 1;
|
||||
Result.InitialSL := Lowest;
|
||||
Highest := Double.MinValue; // Reset for next trend
|
||||
Lowest := Double.MaxValue;
|
||||
end;
|
||||
|
||||
lastHull := hull;
|
||||
lastSma := sma;
|
||||
end
|
||||
else if (hull > sma) and (lastHull <= lastSma) then
|
||||
begin
|
||||
Result.Signal := 1;
|
||||
Result.InitialSL := Lowest;
|
||||
Highest := Double.MinValue; // Reset for next trend
|
||||
Lowest := Double.MaxValue;
|
||||
end;
|
||||
|
||||
lastHull := hull;
|
||||
lastSma := sma;
|
||||
end
|
||||
)
|
||||
);
|
||||
|
||||
// STAGE 2: Position Management. This stateful converter manages the lifecycle
|
||||
@@ -98,95 +97,93 @@ begin
|
||||
var currEntry := Double.NaN;
|
||||
|
||||
var positionManager :=
|
||||
TConverter<TSignalEvent, Double>.CreateAggregation(
|
||||
function(const Value: TSignalEvent; const Broadcast: TConverter<TSignalEvent, Double>.TBroadcastProc): TState
|
||||
var
|
||||
pnl: Double;
|
||||
begin
|
||||
Result := TState.Null;
|
||||
pnl := Double.NaN;
|
||||
|
||||
// 1. Check for a new signal to open or reverse a position
|
||||
if Value.Signal <> 0 then
|
||||
signalGenerator.Chain<Double>(
|
||||
TConverter<TSignalEvent, Double>.CreateAggregation(
|
||||
function(const Value: TSignalEvent; const Broadcast: TConverter<TSignalEvent, Double>.TBroadcastProc): TState
|
||||
var
|
||||
pnl: Double;
|
||||
begin
|
||||
// If a position is already open, close it first
|
||||
if currSig > 0 then
|
||||
pnl := Value.Close - currEntry
|
||||
else if currSig < 0 then
|
||||
pnl := currEntry - Value.Close;
|
||||
Result := TState.Null;
|
||||
pnl := Double.NaN;
|
||||
|
||||
// Open new position
|
||||
currSig := Value.Signal;
|
||||
currEntry := Value.Close;
|
||||
currSL := Value.InitialSL;
|
||||
end
|
||||
// 2. If no new signal, manage the currently open position
|
||||
else
|
||||
begin
|
||||
var atrValue := 15 * Value.ATR;
|
||||
if currSig > 0 then // Manage long position
|
||||
// 1. Check for a new signal to open or reverse a position
|
||||
if Value.Signal <> 0 then
|
||||
begin
|
||||
if Value.Close > currSL then
|
||||
if currSL < Value.Close - atrValue then
|
||||
currSL := Value.Close - atrValue;
|
||||
// If a position is already open, close it first
|
||||
if currSig > 0 then
|
||||
pnl := Value.Close - currEntry
|
||||
else if currSig < 0 then
|
||||
pnl := currEntry - Value.Close;
|
||||
|
||||
if Value.Low <= currSL then
|
||||
begin
|
||||
pnl := currSL - currEntry;
|
||||
currSig := 0; // Close position
|
||||
end;
|
||||
// Open new position
|
||||
currSig := Value.Signal;
|
||||
currEntry := Value.Close;
|
||||
currSL := Value.InitialSL;
|
||||
end
|
||||
else if currSig < 0 then // Manage short position
|
||||
// 2. If no new signal, manage the currently open position
|
||||
else
|
||||
begin
|
||||
if Value.Close < currSL then
|
||||
if currSL > Value.Close + atrValue then
|
||||
currSL := Value.Close + atrValue;
|
||||
|
||||
if Value.High >= currSL then
|
||||
var atrValue := 15 * Value.ATR;
|
||||
if currSig > 0 then // Manage long position
|
||||
begin
|
||||
pnl := currEntry - currSL;
|
||||
currSig := 0; // Close position
|
||||
if Value.Close > currSL then
|
||||
if currSL < Value.Close - atrValue then
|
||||
currSL := Value.Close - atrValue;
|
||||
|
||||
if Value.Low <= currSL then
|
||||
begin
|
||||
pnl := currSL - currEntry;
|
||||
currSig := 0; // Close position
|
||||
end;
|
||||
end
|
||||
else if currSig < 0 then // Manage short position
|
||||
begin
|
||||
if Value.Close < currSL then
|
||||
if currSL > Value.Close + atrValue then
|
||||
currSL := Value.Close + atrValue;
|
||||
|
||||
if Value.High >= currSL then
|
||||
begin
|
||||
pnl := currEntry - currSL;
|
||||
currSig := 0; // Close position
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// 3. If a PnL was generated (trade closed), broadcast it
|
||||
if not IsNan(pnl) then
|
||||
begin
|
||||
currSL := Double.NaN;
|
||||
Broadcast(pnl);
|
||||
end;
|
||||
end
|
||||
// 3. If a PnL was generated (trade closed), broadcast it
|
||||
if not IsNan(pnl) then
|
||||
begin
|
||||
currSL := Double.NaN;
|
||||
Broadcast(pnl);
|
||||
end;
|
||||
end
|
||||
)
|
||||
);
|
||||
|
||||
// Chain the stages together
|
||||
conv.Link(signalGenerator);
|
||||
signalGenerator.Sender.Link(positionManager);
|
||||
|
||||
// The final equity calculation remains the same, it just consumes the PnL from the position manager
|
||||
var FEquity: Double := 10000;
|
||||
var FInit: Boolean := false;
|
||||
var equity :=
|
||||
TConverter<Double, Double>.CreateAggregation(
|
||||
function(const Value: Double; const Broadcast: TConverter<Double, Double>.TBroadcastProc): TState
|
||||
begin
|
||||
if not FInit then
|
||||
positionManager.Chain<Double>(
|
||||
TConverter<Double, Double>.CreateAggregation(
|
||||
function(const Value: Double; const Broadcast: TConverter<Double, Double>.TBroadcastProc): TState
|
||||
begin
|
||||
FInit := true;
|
||||
Broadcast(FEquity);
|
||||
end;
|
||||
if not FInit then
|
||||
begin
|
||||
FInit := true;
|
||||
Broadcast(FEquity);
|
||||
end;
|
||||
|
||||
if not IsNan(Value) then
|
||||
begin
|
||||
FEquity := FEquity + Value;
|
||||
Result := Broadcast(FEquity);
|
||||
end;
|
||||
end
|
||||
if not IsNan(Value) then
|
||||
begin
|
||||
FEquity := FEquity + Value;
|
||||
Result := Broadcast(FEquity);
|
||||
end;
|
||||
end
|
||||
)
|
||||
);
|
||||
|
||||
positionManager.Sender.Link(equity);
|
||||
|
||||
Result := TConverter<TDataPoint<TOhlcItem>, Double>.Construct(ticker, equity.Sender);
|
||||
Result := TConverter<TDataPoint<TOhlcItem>, Double>.Construct(ticker, equity);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -272,7 +272,7 @@ begin
|
||||
inherited Create(AParent);
|
||||
FDataProvider := ADataProvider;
|
||||
|
||||
FReceiver := TConverter.CreateEndpoint<T>(FDataProvider, Owner.Lookback.Value);
|
||||
FReceiver := FDataProvider.CreateEndpoint(Owner.Lookback.Value);
|
||||
end;
|
||||
|
||||
function TChartCustomLayer<T>.GetCount: Int64;
|
||||
@@ -296,7 +296,7 @@ constructor TChartXAxisLayer<T>.Create(AOwner: TMycChart; const ADataProvider: T
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FDataProvider := ADataProvider;
|
||||
FReceiver := TConverter.CreateEndpoint<T>(FDataProvider, Owner.Lookback.Value);
|
||||
FReceiver := FDataProvider.CreateEndpoint(Owner.Lookback.Value);
|
||||
end;
|
||||
|
||||
function TChartXAxisLayer<T>.GetCaption(Idx: Int64): String;
|
||||
|
||||
@@ -636,9 +636,7 @@ function TMycChart.SetXAxisCounter<T>(const DataProvider: TDataProvider<T>): TMy
|
||||
begin
|
||||
FXAxisSeries.Free;
|
||||
|
||||
var counter := TConverter.CreateCounter<T>;
|
||||
DataProvider.Link(counter);
|
||||
FXAxisSeries := TChartXAxisLayer<Int64>.Create(Self, counter.Sender);
|
||||
FXAxisSeries := TChartXAxisLayer<Int64>.Create(Self, DataProvider.Chain<Int64>(TConverter.CreateCounter<T>));
|
||||
Result := FXAxisSeries;
|
||||
end;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ type
|
||||
end;
|
||||
|
||||
// Concrete data provider that manages a list of processors (listeners).
|
||||
TMycContainedDataProvider<T> = class abstract(TContainedObject, TDataProvider<T>.IDataProvider)
|
||||
TMycContainedDataProvider<T> = class abstract(TContainedObject, IDataProvider<T>)
|
||||
private
|
||||
FListeners: TMycNotifyList<IMycProcessor<T>>;
|
||||
public
|
||||
@@ -31,9 +31,9 @@ type
|
||||
// Notifies all linked processors.
|
||||
function Broadcast(const Value: T): TState;
|
||||
// Link a Processor
|
||||
function Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
function Link(const Processor: IMycProcessor<T>): TTag;
|
||||
// Unlink a linked strategy
|
||||
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
TMycSequence<T> = class(TMycProcessor<T>, IMycDataSequence<T>)
|
||||
@@ -50,17 +50,17 @@ type
|
||||
end;
|
||||
|
||||
// Null object implementation for IDataProvider.
|
||||
TNullDataProvider<T> = class(TInterfacedObject, TDataProvider<T>.IDataProvider)
|
||||
TNullDataProvider<T> = class(TInterfacedObject, IDataProvider<T>)
|
||||
public
|
||||
function Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
||||
function Link(const Receiver: IMycProcessor<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
// Abstract base class for components that process data of type S and provide data of type T.
|
||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, TConverter<S, T>.IConverter)
|
||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, IConverter<S, T>)
|
||||
private
|
||||
FSender: TMycContainedDataProvider<T>;
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
function GetSender: IDataProvider<T>;
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override; abstract;
|
||||
// Broadcasts the given data to all linked processors.
|
||||
@@ -68,13 +68,13 @@ type
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||
property Sender: IDataProvider<T> read GetSender;
|
||||
end;
|
||||
|
||||
// Null object implementation for IConverter.
|
||||
TNullConverter<S, T> = class(TInterfacedObject, TConverter<S, T>.IConverter)
|
||||
TNullConverter<S, T> = class(TInterfacedObject, IConverter<S, T>)
|
||||
private
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
function GetSender: IDataProvider<T>;
|
||||
public
|
||||
function ProcessData(const Value: S): TState;
|
||||
end;
|
||||
@@ -174,8 +174,8 @@ type
|
||||
end;
|
||||
private
|
||||
FProcessor: TMycContainedProcessor<T>;
|
||||
FTag: TDataProvider<T>.TTag;
|
||||
FDataProvider: TDataProvider<T>;
|
||||
FTag: TTag;
|
||||
FDataProvider: IDataProvider<T>;
|
||||
FLookback: Int64;
|
||||
FChanged: TFlag;
|
||||
FLock: TLightweightMREW;
|
||||
@@ -225,12 +225,12 @@ type
|
||||
end;
|
||||
|
||||
// Endpoint that collects data into a series.
|
||||
TMycDataJoin<T> = class(TInterfacedObject, TDataProvider<TArray<T>>.IDataProvider)
|
||||
TMycDataJoin<T> = class(TInterfacedObject, IDataProvider<TArray<T>>)
|
||||
private
|
||||
FReceivers: array of record
|
||||
DataProvider: TDataProvider<T>;
|
||||
DataProvider: IDataProvider<T>;
|
||||
Processor: TMycContainedProcessor<T>;
|
||||
Tag: TDataProvider<T>.TTag;
|
||||
Tag: TTag;
|
||||
Queue: TQueue<T>;
|
||||
end;
|
||||
|
||||
@@ -241,16 +241,16 @@ type
|
||||
public
|
||||
constructor Create(const ADataProviders: TArray<TDataProvider<T>>);
|
||||
destructor Destroy; override;
|
||||
property Sender: TMycContainedDataProvider<TArray<T>> read FSender implements TDataProvider<TArray<T>>.IDataProvider;
|
||||
property Sender: TMycContainedDataProvider<TArray<T>> read FSender implements IDataProvider<TArray<T>>;
|
||||
end;
|
||||
|
||||
TMycComposedConverter<S, T> = class(TMycProcessor<S>, TConverter<S, T>.IConverter)
|
||||
TMycComposedConverter<S, T> = class(TMycProcessor<S>, IConverter<S, T>)
|
||||
private
|
||||
FProcessor: IMycProcessor<S>;
|
||||
FDataProvider: TDataProvider<T>;
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override;
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
function GetSender: IDataProvider<T>;
|
||||
public
|
||||
constructor Create(const AProcessor: IMycProcessor<S>; const ADataProvider: TDataProvider<T>);
|
||||
end;
|
||||
@@ -314,7 +314,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMycContainedDataProvider<T>.Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
function TMycContainedDataProvider<T>.Link(const Processor: IMycProcessor<T>): TTag;
|
||||
begin
|
||||
// Add the Processor to the notification list
|
||||
FListeners.Lock;
|
||||
@@ -325,7 +325,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycContainedDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
||||
procedure TMycContainedDataProvider<T>.Unlink(Tag: TTag);
|
||||
begin
|
||||
FListeners.Lock;
|
||||
try
|
||||
@@ -337,12 +337,12 @@ end;
|
||||
|
||||
{ TNullDataProvider<T> }
|
||||
|
||||
function TNullDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
function TNullDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TTag;
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
procedure TNullDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
||||
procedure TNullDataProvider<T>.Unlink(Tag: TTag);
|
||||
begin
|
||||
// Do nothing in the null implementation.
|
||||
end;
|
||||
@@ -366,14 +366,14 @@ begin
|
||||
Result := FSender.Broadcast(Value);
|
||||
end;
|
||||
|
||||
function TMycConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
|
||||
function TMycConverter<S, T>.GetSender: IDataProvider<T>;
|
||||
begin
|
||||
Result := FSender;
|
||||
end;
|
||||
|
||||
{ TNullConverter<S, T> }
|
||||
|
||||
function TNullConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
|
||||
function TNullConverter<S, T>.GetSender: IDataProvider<T>;
|
||||
begin
|
||||
Result := TDataProvider<T>.Null;
|
||||
end;
|
||||
@@ -499,7 +499,6 @@ begin
|
||||
|
||||
FProcessor := TMycContainedProcessor<T>.Create(Self, ProcessData);
|
||||
FTag := FDataProvider.Link(FProcessor);
|
||||
|
||||
end;
|
||||
|
||||
destructor TMycDataEndpoint<T>.Destroy;
|
||||
@@ -930,7 +929,7 @@ begin
|
||||
FDataProvider := ADataProvider;
|
||||
end;
|
||||
|
||||
function TMycComposedConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
|
||||
function TMycComposedConverter<S, T>.GetSender: IDataProvider<T>;
|
||||
begin
|
||||
Result := FDataProvider;
|
||||
end;
|
||||
|
||||
+88
-92
@@ -15,38 +15,60 @@ type
|
||||
function ProcessData(const Value: T): TState;
|
||||
end;
|
||||
|
||||
TTag = Pointer;
|
||||
IDataProvider<T> = interface
|
||||
function Link(const Receiver: IMycProcessor<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
end;
|
||||
|
||||
IConverter<S, T> = interface(IMycProcessor<S>)
|
||||
function GetSender: IDataProvider<T>;
|
||||
property Sender: IDataProvider<T> read GetSender;
|
||||
end;
|
||||
|
||||
// Interface helper for IDataProvider providing the null object pattern.
|
||||
TDataProvider<T> = record
|
||||
public
|
||||
type
|
||||
TTag = Pointer;
|
||||
IDataProvider = interface
|
||||
function Link(const Receiver: IMycProcessor<T>): TTag;
|
||||
procedure Unlink(Tag: TTag);
|
||||
TLink = record
|
||||
private
|
||||
FDataProvider: IDataProvider<T>;
|
||||
FTag: TTag;
|
||||
public
|
||||
procedure Unlink;
|
||||
property DataProvider: IDataProvider<T> read FDataProvider;
|
||||
end;
|
||||
|
||||
strict private
|
||||
class var
|
||||
FNull: IDataProvider;
|
||||
FNull: IDataProvider<T>;
|
||||
class constructor CreateClass;
|
||||
|
||||
private
|
||||
FDataProvider: IDataProvider;
|
||||
FDataProvider: IDataProvider<T>;
|
||||
|
||||
public
|
||||
constructor Create(const ADataProvider: IDataProvider);
|
||||
constructor Create(const ADataProvider: IDataProvider<T>);
|
||||
|
||||
// Managed record operators
|
||||
class operator Initialize(out Dest: TDataProvider<T>);
|
||||
class operator Implicit(const A: IDataProvider): TDataProvider<T>; overload;
|
||||
class operator Implicit(const A: TDataProvider<T>): IDataProvider; overload;
|
||||
class operator Implicit(const A: IDataProvider<T>): TDataProvider<T>; overload;
|
||||
class operator Implicit(const A: TDataProvider<T>): IDataProvider<T>; overload;
|
||||
|
||||
// Wrapper for IMycDataProvider methods
|
||||
function Link(const Receiver: IMycProcessor<T>): TTag; inline;
|
||||
procedure Unlink(Tag: TTag); inline;
|
||||
function Link(const Receiver: IMycProcessor<T>): TLink; inline;
|
||||
|
||||
function Chain(const Next: IMycProcessor<T>): IMycProcessor<T>; overload; inline;
|
||||
function Chain<R>(const Next: IConverter<T, R>): TDataProvider<R>; overload; inline;
|
||||
function Chain<R>(const Func: TConstFunc<T, R>): TDataProvider<R>; overload; inline;
|
||||
|
||||
function CreateEndpoint(Lookback: Int64): TLazy<TSeries<T>>;
|
||||
|
||||
// Extracts the field of a record by it's name (using RTTI).
|
||||
function Field<R>(const FieldName: String): TDataProvider<R>; inline;
|
||||
|
||||
function MakeParallel: TDataProvider<T>; inline;
|
||||
|
||||
// Provides access to the null object instance.
|
||||
class property Null: IDataProvider read FNull;
|
||||
class property Null: IDataProvider<T> read FNull;
|
||||
end;
|
||||
|
||||
IMycDataSequence<T> = interface(IMycProcessor<T>)
|
||||
@@ -60,57 +82,38 @@ type
|
||||
TConverter<S, T> = record
|
||||
public
|
||||
type
|
||||
IConverter = interface(IMycProcessor<S>)
|
||||
{$region 'private'}
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
{$endregion}
|
||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||
end;
|
||||
|
||||
TBroadcastProc = reference to function(const Value: T): TState;
|
||||
|
||||
strict private
|
||||
class var
|
||||
FNull: IConverter;
|
||||
FNull: IConverter<S, T>;
|
||||
class constructor CreateClass;
|
||||
private
|
||||
FConverter: IConverter;
|
||||
FConverter: IConverter<S, T>;
|
||||
function GetSender: TDataProvider<T>; inline;
|
||||
public
|
||||
constructor Create(const AConverter: IConverter);
|
||||
constructor Create(const AConverter: IConverter<S, T>);
|
||||
|
||||
// Managed record operators
|
||||
class operator Initialize(out Dest: TConverter<S, T>);
|
||||
class operator Implicit(const A: IConverter): TConverter<S, T>; overload;
|
||||
class operator Implicit(const A: TConverter<S, T>): IConverter; overload;
|
||||
class operator Implicit(const A: IConverter<S, T>): TConverter<S, T>; overload;
|
||||
class operator Implicit(const A: TConverter<S, T>): IConverter<S, T>; overload;
|
||||
|
||||
class function Construct(const Processor: IMycProcessor<S>; const DataProvider: TDataProvider<T>): TConverter<S, T>; static;
|
||||
class function CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
|
||||
class function CreateAggregation(const Func: TConstFunc<S, TBroadcastProc, TState>): TConverter<S, T>; static;
|
||||
class function CreateParallel(const Func: TConstFunc<S, T>): TConverter<S, T>; static;
|
||||
|
||||
function Chain<R>(const Next: TConverter<T, R>): TConverter<T, R>; overload; inline;
|
||||
function Chain<R>(const Func: TConstFunc<T, R>): TConverter<T, R>; overload; inline;
|
||||
function ChainParallel<R>(const Func: TConstFunc<T, R>): TConverter<T, R>; overload; inline;
|
||||
|
||||
function MakeParallel: TConverter<T, T>; overload; inline;
|
||||
|
||||
// Extracts the field of a record by it's name (using RTTI).
|
||||
function Field<R>(const FieldName: String): TConverter<T, R>; overload; inline;
|
||||
|
||||
function Sequence(Count: Integer): IMycDataSequence<T>; overload;
|
||||
function Sequence(const Items: TArray<IConverter>): IMycDataSequence<S>; overload;
|
||||
function Sequence(const Items: TArray<IConverter<S, T>>): IMycDataSequence<S>; overload;
|
||||
|
||||
// Provides access to the null object instance.
|
||||
class property Null: IConverter read FNull;
|
||||
class property Null: IConverter<S, T> read FNull;
|
||||
// Wrapper for IConverter.Sender
|
||||
property Sender: TDataProvider<T> read GetSender;
|
||||
end;
|
||||
|
||||
// Factory for creating specific converter instances.
|
||||
TConverter = record
|
||||
class function CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TLazy<TSeries<T>>; static;
|
||||
|
||||
class function CreateCounter<T>: TConverter<T, Int64>; static;
|
||||
class function CreateTicker<T>: TConverter<TArray<T>, T>; static;
|
||||
class function CreateRecordField<S, T>(const FieldName: String): TConverter<S, T>; static;
|
||||
@@ -123,8 +126,6 @@ type
|
||||
|
||||
class function CreateSequence<T>(Count: Integer; const Parent: TDataProvider<T>): TArray<TConverter<T, T>>; overload; static;
|
||||
|
||||
class function Parallel<T>(Parent: TDataProvider<T>): TConverter<T, T>; static;
|
||||
|
||||
class function FieldToRecord<T>(const Layout: TDataRecord.TLayout; const Name: String): TConverter<T, TDataRecord>; static;
|
||||
class function FieldOfRecord<T>(const Layout: TDataRecord.TLayout; const Name: String): TConverter<TDataRecord, T>; static;
|
||||
|
||||
@@ -163,36 +164,64 @@ begin
|
||||
FNull := TNullDataProvider<T>.Create;
|
||||
end;
|
||||
|
||||
constructor TDataProvider<T>.Create(const ADataProvider: IDataProvider);
|
||||
constructor TDataProvider<T>.Create(const ADataProvider: IDataProvider<T>);
|
||||
begin
|
||||
FDataProvider := ADataProvider;
|
||||
if not Assigned(FDataProvider) then
|
||||
FDataProvider := FNull;
|
||||
end;
|
||||
|
||||
function TDataProvider<T>.Chain(const Next: IMycProcessor<T>): IMycProcessor<T>;
|
||||
begin
|
||||
FDataProvider.Link(Next);
|
||||
Result := Next;
|
||||
end;
|
||||
|
||||
function TDataProvider<T>.Chain<R>(const Next: IConverter<T, R>): TDataProvider<R>;
|
||||
begin
|
||||
FDataProvider.Link(Next);
|
||||
Result := Next.Sender;
|
||||
end;
|
||||
|
||||
function TDataProvider<T>.Chain<R>(const Func: TConstFunc<T, R>): TDataProvider<R>;
|
||||
begin
|
||||
Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func));
|
||||
end;
|
||||
|
||||
function TDataProvider<T>.CreateEndpoint(Lookback: Int64): TLazy<TSeries<T>>;
|
||||
begin
|
||||
Result := TMycDataEndpoint<T>.Create(FDataProvider, Lookback);
|
||||
end;
|
||||
|
||||
function TDataProvider<T>.Field<R>(const FieldName: String): TDataProvider<R>;
|
||||
begin
|
||||
Result := Chain<R>(TMycRecordFieldReader<T, R>.Create(FieldName));
|
||||
end;
|
||||
|
||||
class operator TDataProvider<T>.Initialize(out Dest: TDataProvider<T>);
|
||||
begin
|
||||
Dest.FDataProvider := FNull;
|
||||
end;
|
||||
|
||||
class operator TDataProvider<T>.Implicit(const A: IDataProvider): TDataProvider<T>;
|
||||
class operator TDataProvider<T>.Implicit(const A: IDataProvider<T>): TDataProvider<T>;
|
||||
begin
|
||||
Result.Create(A);
|
||||
end;
|
||||
|
||||
class operator TDataProvider<T>.Implicit(const A: TDataProvider<T>): IDataProvider;
|
||||
class operator TDataProvider<T>.Implicit(const A: TDataProvider<T>): IDataProvider<T>;
|
||||
begin
|
||||
Result := A.FDataProvider;
|
||||
end;
|
||||
|
||||
function TDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TTag;
|
||||
function TDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TLink;
|
||||
begin
|
||||
Result := FDataProvider.Link(Receiver);
|
||||
Result.FDataProvider := FDataProvider;
|
||||
Result.FTag := FDataProvider.Link(Receiver);
|
||||
end;
|
||||
|
||||
procedure TDataProvider<T>.Unlink(Tag: TTag);
|
||||
function TDataProvider<T>.MakeParallel: TDataProvider<T>;
|
||||
begin
|
||||
FDataProvider.Unlink(Tag);
|
||||
Result := Chain<T>(TMycParallelConverter<T>.Create as IConverter<T, T>);
|
||||
end;
|
||||
|
||||
{ TConverter<S, T> }
|
||||
@@ -202,29 +231,13 @@ begin
|
||||
FNull := TNullConverter<S, T>.Create;
|
||||
end;
|
||||
|
||||
constructor TConverter<S, T>.Create(const AConverter: IConverter);
|
||||
constructor TConverter<S, T>.Create(const AConverter: IConverter<S, T>);
|
||||
begin
|
||||
FConverter := AConverter;
|
||||
if not Assigned(FConverter) then
|
||||
FConverter := FNull;
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.Chain<R>(const Next: TConverter<T, R>): TConverter<T, R>;
|
||||
begin
|
||||
FConverter.Sender.Link(Next);
|
||||
Result := Next;
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.Chain<R>(const Func: TConstFunc<T, R>): TConverter<T, R>;
|
||||
begin
|
||||
Result := Chain<R>(TMycGenericConverter<T, R>.Create(Func));
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.ChainParallel<R>(const Func: TConstFunc<T, R>): TConverter<T, R>;
|
||||
begin
|
||||
Result := Chain<R>(TMycGenericParallelConverter<T, R>.Create(Func));
|
||||
end;
|
||||
|
||||
class function TConverter<S, T>.Construct(const Processor: IMycProcessor<S>; const DataProvider: TDataProvider<T>): TConverter<S, T>;
|
||||
begin
|
||||
Result := TMycComposedConverter<S, T>.Create(Processor, DataProvider);
|
||||
@@ -240,33 +253,18 @@ begin
|
||||
Result := TMycGenericConverter<S, T>.Create(Func);
|
||||
end;
|
||||
|
||||
class function TConverter<S, T>.CreateParallel(const Func: TConstFunc<S, T>): TConverter<S, T>;
|
||||
begin
|
||||
Result := TMycGenericParallelConverter<S, T>.Create(Func);
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.Field<R>(const FieldName: String): TConverter<T, R>;
|
||||
begin
|
||||
Result := Chain<R>(TConverter.CreateRecordField<T, R>(FieldName));
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.GetSender: TDataProvider<T>;
|
||||
begin
|
||||
Result := FConverter.Sender;
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.MakeParallel: TConverter<T, T>;
|
||||
begin
|
||||
Result := Chain<T>(TMycGenericParallelConverter<T, T>.Create(function(const Value: T): T begin Result := Value; end));
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.Sequence(Count: Integer): IMycDataSequence<T>;
|
||||
begin
|
||||
Result := TMycSequence<T>.Create(Count);
|
||||
FConverter.Sender.Link(Result);
|
||||
end;
|
||||
|
||||
function TConverter<S, T>.Sequence(const Items: TArray<IConverter>): IMycDataSequence<S>;
|
||||
function TConverter<S, T>.Sequence(const Items: TArray<IConverter<S, T>>): IMycDataSequence<S>;
|
||||
begin
|
||||
var seq: IMycDataSequence<S> := TMycSequence<S>.Create(Length(Items));
|
||||
|
||||
@@ -281,12 +279,12 @@ begin
|
||||
Dest.FConverter := FNull;
|
||||
end;
|
||||
|
||||
class operator TConverter<S, T>.Implicit(const A: IConverter): TConverter<S, T>;
|
||||
class operator TConverter<S, T>.Implicit(const A: IConverter<S, T>): TConverter<S, T>;
|
||||
begin
|
||||
Result.Create(A);
|
||||
end;
|
||||
|
||||
class operator TConverter<S, T>.Implicit(const A: TConverter<S, T>): IConverter;
|
||||
class operator TConverter<S, T>.Implicit(const A: TConverter<S, T>): IConverter<S, T>;
|
||||
begin
|
||||
Result := A.FConverter;
|
||||
end;
|
||||
@@ -316,11 +314,6 @@ begin
|
||||
);
|
||||
end;
|
||||
|
||||
class function TConverter.CreateEndpoint<T>(const DataProvider: TDataProvider<T>; Lookback: Int64): TLazy<TSeries<T>>;
|
||||
begin
|
||||
Result := TMycDataEndpoint<T>.Create(DataProvider, Lookback);
|
||||
end;
|
||||
|
||||
class function TConverter.CreateIdentity<T>: TConverter<T, T>;
|
||||
begin
|
||||
Result := TMycIdentityConverter<T>.Create;
|
||||
@@ -423,10 +416,13 @@ begin
|
||||
RecProvider.Link(Result);
|
||||
end;
|
||||
|
||||
class function TConverter.Parallel<T>(Parent: TDataProvider<T>): TConverter<T, T>;
|
||||
procedure TDataProvider<T>.TLink.Unlink;
|
||||
begin
|
||||
Result := TMycParallelConverter<T>.Create;
|
||||
Parent.Link(Result);
|
||||
if FTag <> nil then
|
||||
begin
|
||||
FDataProvider.Unlink(FTag);
|
||||
FTag := nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user