New Test-Startegy

This commit is contained in:
Michael Schimmel
2025-07-22 17:21:59 +02:00
parent c530f2fc0b
commit e6d41260f9
6 changed files with 311 additions and 42 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<ProjectVersion>20.3</ProjectVersion>
<FrameworkType>FMX</FrameworkType>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Config Condition="'$(Config)'==''">Release</Config>
<Platform Condition="'$(Platform)'==''">Win64</Platform>
<ProjectName Condition="'$(ProjectName)'==''">AuraTrader</ProjectName>
<TargetedPlatforms>3</TargetedPlatforms>
+57 -41
View File
@@ -52,7 +52,8 @@ uses
DynamicFMXControl,
Myc.FMX.Chart,
Myc.Trade.Indicators,
Myc.DataRecord;
Myc.DataRecord,
StrategyTest;
type
TForm1 = class(TForm)
@@ -117,16 +118,6 @@ type
property OnEvent: TNotifyEvent read FOnEvent write FOnEvent;
end;
TEquitySum = class(TMycConverter<Double, Double>)
private
FEquity: Double;
FInit: Boolean;
protected
function ProcessData(const Value: Double): TState; override;
public
constructor Create(AEquity: Double);
end;
var
Form1: TForm1;
@@ -426,7 +417,28 @@ begin
var pnl := Signal.Field<Double>('pnl');
var equity: TConverter<Double, Double> := TEquitySum.Create(10000);
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>;
@@ -635,7 +647,27 @@ begin
var pnl := Signal.Field<Double>('pnl');
var equity: TConverter<Double, Double> := TEquitySum.Create(10000);
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
begin
FInit := true;
Broadcast(FEquity);
end;
if not IsNan(Value) then
begin
FEquity := FEquity + Value;
Result := Broadcast(FEquity);
end;
end
);
pnl.Sender.Link(equity);
var Layout := CurrLayout<TVertScrollBox>;
@@ -867,38 +899,22 @@ begin
var timeframe := TTimeframe.M15;
ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
// timeframe := M5;
// ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
// timeframe := H4;
// ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
// timeframe := D;
// ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
// timeframe := M;
// ExecuteStrategy(Symbol, timeframe, CreateStrategy2(timeframe));
end;
var tstStrat := StrategyTest.CreateStrategy1(timeframe);
ExecuteStrategy(Symbol, timeframe, tstStrat);
{ TEquitySum<S, T> }
var Layout := CurrLayout<TVertScrollBox>;
if Layout = nil then
exit;
constructor TEquitySum.Create(AEquity: Double);
begin
inherited Create;
FEquity := AEquity;
FInit := false;
end;
var pnlChart := TMycChart.Create(Self);
AlignControl(pnlChart);
pnlChart.Height := Layout.ChildrenRect.Width * 9 / 24;
pnlChart.Lookback.Value := 50000;
function TEquitySum.ProcessData(const Value: Double): TState;
begin
if not FInit then
begin
FInit := true;
Broadcast(FEquity);
end;
pnlChart.SetXAxisCounter<Double>(tstStrat.Sender);
if not IsNan(Value) then
begin
FEquity := FEquity + Value;
Result := Broadcast(FEquity);
end;
var panel := pnlChart.AddPanel;
panel.AddDoubleSeries(tstStrat.Sender, TAlphaColors.Blue, 3);
end;
end.
+190
View File
@@ -0,0 +1,190 @@
unit StrategyTest;
interface
uses
Myc.Signals,
Myc.Trade.Types,
Myc.Trade.DataPoint,
Myc.Trade.DataArray,
Myc.DataRecord,
Myc.Trade.Indicators;
function CreateStrategy1(Timeframe: TTimeframe): TConverter<TDataPoint<TOhlcItem>, Double>;
implementation
uses
System.SysUtils,
System.Math;
function CreateStrategy1(Timeframe: TTimeframe): TConverter<TDataPoint<TOhlcItem>, Double>;
type
TSignal = record
Sig: Double;
SL: Double;
Entry: Double;
pnl: Double;
end;
begin
var ticker := TConverter.CreateIdentity<TDataPoint<TOhlcItem>>;
var OhlcPoint := ticker.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateOhlcAggregation(Timeframe));
var Ohlc := OhlcPoint.Field<TOhlcItem>('Data');
var Closes := Ohlc.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.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 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 Signal :=
TConverter<TArray<Double>, TSignal>.CreateGeneric(
function(const Values: TArray<Double>): TSignal
begin
var low := Values[0];
var high := Values[1];
var close := Values[2];
var atr := Values[3];
var hull := Values[4];
var sma := Values[5];
if low < Lowest then
Lowest := low;
if high > Highest then
Highest := high;
Result := curr;
Result.Sig := 0;
var pnl: double := NaN;
if (hull < sma) and (lastHull >= lastSma) then
begin
if curr.Sig > 0 then
pnl := close - curr.Entry;
curr.Sig := -1;
curr.SL := Highest;
curr.Entry := close;
Result := curr;
end
else if (hull > sma) and (lastHull <= lastSma) then
begin
if curr.Sig < 0 then
pnl := curr.Entry - close;
curr.Sig := 1;
curr.SL := Lowest;
curr.Entry := close;
Result := curr;
end;
atr := 15 * atr;
if curr.Sig > 0 then
begin
if close > curr.SL then
begin
if curr.SL < close - atr then
curr.SL := close - atr;
Result.SL := curr.SL;
end;
if 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 close < curr.SL then
begin
if curr.SL > close + atr then
curr.SL := close + atr;
Result.SL := curr.SL;
end;
if 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;
lastHull := hull;
lastSma := sma;
end
);
conv.Link(Signal);
var pnl := Signal.Field<Double>('pnl');
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
begin
FInit := true;
Broadcast(FEquity);
end;
if not IsNan(Value) then
begin
FEquity := FEquity + Value;
Result := Broadcast(FEquity);
end;
end
);
pnl.Sender.Link(equity);
Result := TConverter<TDataPoint<TOhlcItem>, Double>.Construct(ticker, equity.Sender);
end;
end.
+48
View File
@@ -89,6 +89,15 @@ type
constructor Create(const AFunc: TConstFunc<S, T>);
end;
TMycGenericAggregator<S, T> = class(TMycConverter<S, T>)
private
FFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>;
protected
function ProcessData(const Value: S): TState; override;
public
constructor Create(const AFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>);
end;
TMycGenericParallelConverter<S, T> = class(TMycConverter<S, T>)
private
FFunc: TConstFunc<S, T>;
@@ -235,6 +244,17 @@ type
property Sender: TMycContainedDataProvider<TArray<T>> read FSender implements TDataProvider<TArray<T>>.IDataProvider;
end;
TMycComposedConverter<S, T> = class(TMycProcessor<S>, TConverter<S, T>.IConverter)
private
FProcessor: IMycProcessor<S>;
FDataProvider: TDataProvider<T>;
protected
function ProcessData(const Value: S): TState; override;
function GetSender: TDataProvider<T>.IDataProvider;
public
constructor Create(const AProcessor: IMycProcessor<S>; const ADataProvider: TDataProvider<T>);
end;
implementation
uses
@@ -892,4 +912,32 @@ begin
FSender.Broadcast(Arr);
end;
constructor TMycGenericAggregator<S, T>.Create(const AFunc: TConstFunc<S, TConverter<S, T>.TBroadcastProc, TState>);
begin
inherited Create;
FFunc := AFunc;
end;
function TMycGenericAggregator<S, T>.ProcessData(const Value: S): TState;
begin
Result := FFunc(Value, function(const Value: T): TState begin Result := FSender.Broadcast(Value); end);
end;
constructor TMycComposedConverter<S, T>.Create(const AProcessor: IMycProcessor<S>; const ADataProvider: TDataProvider<T>);
begin
inherited Create;
FProcessor := AProcessor;
FDataProvider := ADataProvider;
end;
function TMycComposedConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
begin
Result := FDataProvider;
end;
function TMycComposedConverter<S, T>.ProcessData(const Value: S): TState;
begin
Result := FProcessor.ProcessData(Value);
end;
end.
+14
View File
@@ -67,6 +67,8 @@ type
property Sender: TDataProvider<T>.IDataProvider read GetSender;
end;
TBroadcastProc = reference to function(const Value: T): TState;
strict private
class var
FNull: IConverter;
@@ -82,7 +84,9 @@ type
class operator Implicit(const A: IConverter): TConverter<S, T>; overload;
class operator Implicit(const A: TConverter<S, T>): IConverter; 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;
@@ -221,6 +225,16 @@ 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);
end;
class function TConverter<S, T>.CreateAggregation(const Func: TConstFunc<S, TBroadcastProc, TState>): TConverter<S, T>;
begin
Result := TMycGenericAggregator<S, T>.Create(Func);
end;
class function TConverter<S, T>.CreateGeneric(const Func: TConstFunc<S, T>): TConverter<S, T>;
begin
Result := TMycGenericConverter<S, T>.Create(Func);
+1
View File
@@ -29,6 +29,7 @@ type
end;
TConstFunc<S, T> = reference to function(const Value: S): T;
TConstFunc<S, T, U> = reference to function(const Value1: S; const Value2: T): U;
TConstProc<T> = reference to procedure(const Value: T);
TConstFuncPredicate<S, T> = reference to function(const Value: S; out Res: T): Boolean;