DataFlow refactoring
This commit is contained in:
+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.
|
||||
|
||||
Reference in New Issue
Block a user