Chart X grid, standard timeframes
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -13,8 +13,6 @@ uses
|
||||
Myc.Trade.DataArray;
|
||||
|
||||
type
|
||||
TTimeframe = (M1, M5, H1, D);
|
||||
|
||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||
type
|
||||
TConvertFunc = reference to function(const Value: S): T;
|
||||
@@ -54,11 +52,11 @@ type
|
||||
|
||||
TGenericIndicator<S, T> = class(TIndicator<S, T>)
|
||||
private
|
||||
FFunc: TFunc<S, T>;
|
||||
FFunc: TIndicatorFunc<S, T>;
|
||||
protected
|
||||
function Calculate(const Value: S): T; override; final;
|
||||
public
|
||||
constructor Create(const AFunc: TFunc<S, T>);
|
||||
constructor Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -76,14 +74,49 @@ begin
|
||||
end;
|
||||
|
||||
function TTicksToTimeframe.GetBarStartTime(const TimeStamp: TDateTime; const Timeframe: TTimeframe): TDateTime;
|
||||
var
|
||||
baseTime: TDateTime;
|
||||
begin
|
||||
// Align the time grid to UTC 0:00 using functions from System.DateUtils
|
||||
baseTime := RecodeMilliSecond(TimeStamp, 0);
|
||||
|
||||
case Timeframe of
|
||||
M1: Result := RecodeSecond(RecodeMilliSecond(TimeStamp, 0), 0);
|
||||
M5: Result := RecodeMinute(RecodeSecond(RecodeMilliSecond(TimeStamp, 0), 0), MinuteOf(TimeStamp) - MinuteOf(TimeStamp) mod 5);
|
||||
H1: Result := RecodeMinute(RecodeSecond(RecodeMilliSecond(TimeStamp, 0), 0), 0);
|
||||
S: Result := baseTime;
|
||||
S5: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 5));
|
||||
S15: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 15));
|
||||
S30: Result := RecodeSecond(baseTime, SecondOf(TimeStamp) - (SecondOf(TimeStamp) mod 30));
|
||||
|
||||
M: Result := RecodeSecond(baseTime, 0);
|
||||
M2: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 2));
|
||||
M3: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 3));
|
||||
M5: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 5));
|
||||
M10: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 10));
|
||||
M15: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 15));
|
||||
M30: Result := RecodeMinute(RecodeSecond(baseTime, 0), MinuteOf(TimeStamp) - (MinuteOf(TimeStamp) mod 30));
|
||||
|
||||
H: Result := RecodeMinute(RecodeSecond(baseTime, 0), 0);
|
||||
H2: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 2));
|
||||
H3: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 3));
|
||||
H4: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 4));
|
||||
H8: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 8));
|
||||
H12: Result := RecodeHour(RecodeMinute(RecodeSecond(baseTime, 0), 0), HourOf(TimeStamp) - (HourOf(TimeStamp) mod 12));
|
||||
|
||||
D: Result := StartOfTheDay(TimeStamp);
|
||||
// D2, D3 are uncommon; this is a simple modulo-based approach relative to TDateTime's epoch.
|
||||
D2: Result := Floor(TimeStamp) - (Floor(TimeStamp) mod 2);
|
||||
D3: Result := Floor(TimeStamp) - (Floor(TimeStamp) mod 3);
|
||||
|
||||
W: Result := TimeStamp.StartOfTheWeek;
|
||||
|
||||
MN: Result := TimeStamp.StartOfTheMonth;
|
||||
// Quarter alignment
|
||||
MN3: Result := RecodeMonth(TimeStamp.StartOfTheMonth, (MonthOf(TimeStamp) - 1) div 3 * 3 + 1);
|
||||
// Half-year alignment
|
||||
MN6: Result := RecodeMonth(TimeStamp.StartOfTheMonth, (MonthOf(TimeStamp) - 1) div 6 * 6 + 1);
|
||||
|
||||
Y: Result := TimeStamp.StartOfTheYear;
|
||||
else
|
||||
// Fallback for any undefined timeframe
|
||||
Result := 0;
|
||||
end;
|
||||
end;
|
||||
@@ -122,7 +155,9 @@ begin
|
||||
begin
|
||||
// A new bar starts, so the previous one is now complete.
|
||||
if (lastBarTime > 0) then
|
||||
begin
|
||||
states.Add(Broadcast(FCurrentBar));
|
||||
end;
|
||||
|
||||
// Start a new bar, Volume is 1 because this is the first tick.
|
||||
currentBar := TOhlcItem.Create(midPrice, midPrice, midPrice, midPrice, 1);
|
||||
@@ -161,12 +196,16 @@ begin
|
||||
Result := Broadcast(FFunc(Value));
|
||||
end;
|
||||
|
||||
{ TIndicator<S,T> }
|
||||
|
||||
function TIndicator<S, T>.ProcessData(const Value: S): TState;
|
||||
begin
|
||||
Result := FQueue.Enqueue(function: TState begin Result := Broadcast(Calculate(Value)); end);
|
||||
Result := Broadcast(Calculate(Value));
|
||||
end;
|
||||
|
||||
constructor TGenericIndicator<S, T>.Create(const AFunc: TFunc<S, T>);
|
||||
{ TGenericIndicator<S,T> }
|
||||
|
||||
constructor TGenericIndicator<S, T>.Create(const AFunc: TIndicatorFunc<S, T>);
|
||||
begin
|
||||
inherited Create;
|
||||
FFunc := AFunc;
|
||||
|
||||
+39
-26
@@ -165,17 +165,25 @@ begin
|
||||
|
||||
/////
|
||||
|
||||
var OhlcPoint: IMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>> := TTicksToTimeframe.Create(M1);
|
||||
var timeframe := TTimeframe.S15;
|
||||
|
||||
var OhlcPoint: IMycConverter<TArray<TDataPoint<TAskBidItem>>, TDataPoint<TOhlcItem>> := TTicksToTimeframe.Create(timeframe);
|
||||
|
||||
var Timestamps: IMycConverter<TDataPoint<TOhlcItem>, TDateTime> :=
|
||||
TMycGenericConverter<TDataPoint<TOhlcItem>, TDateTime>
|
||||
.Create(function(const Ohlc: TDataPoint<TOhlcItem>): TDateTime begin Result := Ohlc.Time; end);
|
||||
|
||||
OhlcPoint.Sender.Link(TimeStamps);
|
||||
chart.SetXAxisSeries(timeframe, Timestamps.Sender);
|
||||
|
||||
var Panel := chart.Panels[0];
|
||||
|
||||
var Ohlc: IMycConverter<TDataPoint<TOhlcItem>, TOhlcItem> :=
|
||||
TMycGenericConverter<TDataPoint<TOhlcItem>, TOhlcItem>
|
||||
.Create(function(const Ohlc: TDataPoint<TOhlcItem>): TOhlcItem begin Result := Ohlc.Data; end);
|
||||
|
||||
OhlcPoint.Sender.Link(Ohlc);
|
||||
Panel.AddOhlcSeries(Ohlc.Sender);
|
||||
|
||||
var Closes: IMycConverter<TOhlcItem, Double> :=
|
||||
TMycGenericConverter<TOhlcItem, Double>.Create(function(const Ohlc: TOhlcItem): Double begin Result := Ohlc.Close; end);
|
||||
@@ -184,17 +192,17 @@ begin
|
||||
|
||||
var Hull: IMycConverter<Double, Double> := TGenericIndicator<Double, Double>.Create(TIndicators.CreateHMA(150));
|
||||
Closes.Sender.Link(Hull);
|
||||
chart.Panels[0].AddDoubleSeries(Hull.Sender, TAlphaColors.Aliceblue);
|
||||
Panel.AddDoubleSeries(Hull.Sender, TAlphaColors.Aliceblue);
|
||||
|
||||
// Add SMA (Simple Moving Average)
|
||||
var Sma: IMycConverter<Double, Double> := TGenericIndicator<Double, Double>.Create(TIndicators.CreateSMA(50));
|
||||
Closes.Sender.Link(Sma);
|
||||
chart.Panels[0].AddDoubleSeries(Sma.Sender, TAlphaColors.Yellow);
|
||||
Panel.AddDoubleSeries(Sma.Sender, TAlphaColors.Yellow);
|
||||
|
||||
// Add EMA (Exponential Moving Average)
|
||||
var Ema: IMycConverter<Double, Double> := TGenericIndicator<Double, Double>.Create(TIndicators.CreateEMA(21));
|
||||
Closes.Sender.Link(Ema);
|
||||
chart.Panels[0].AddDoubleSeries(Ema.Sender, TAlphaColors.Aqua);
|
||||
Panel.AddDoubleSeries(Ema.Sender, TAlphaColors.Aqua);
|
||||
|
||||
// Add Bollinger Bands (20, 2.0)
|
||||
var Boli: IMycConverter<Double, TBollingerBandsResult> :=
|
||||
@@ -205,59 +213,64 @@ begin
|
||||
TMycGenericConverter<TBollingerBandsResult, Double>
|
||||
.Create(function(const Item: TBollingerBandsResult): Double begin Result := Item.UpperBand; end);
|
||||
Boli.Sender.Link(BoliUpper);
|
||||
chart.Panels[0].AddDoubleSeries(BoliUpper.Sender, TAlphaColors.Gray);
|
||||
Panel.AddDoubleSeries(BoliUpper.Sender, TAlphaColors.Gray);
|
||||
|
||||
var BoliMiddle: IMycConverter<TBollingerBandsResult, Double> :=
|
||||
TMycGenericConverter<TBollingerBandsResult, Double>
|
||||
.Create(function(const Item: TBollingerBandsResult): Double begin Result := Item.MiddleBand; end);
|
||||
Boli.Sender.Link(BoliMiddle);
|
||||
chart.Panels[0].AddDoubleSeries(BoliMiddle.Sender, TAlphaColors.Darkgray, 1.0);
|
||||
Panel.AddDoubleSeries(BoliMiddle.Sender, TAlphaColors.Darkgray, 1.0);
|
||||
|
||||
var BoliLower: IMycConverter<TBollingerBandsResult, Double> :=
|
||||
TMycGenericConverter<TBollingerBandsResult, Double>
|
||||
.Create(function(const Item: TBollingerBandsResult): Double begin Result := Item.LowerBand; end);
|
||||
Boli.Sender.Link(BoliLower);
|
||||
chart.Panels[0].AddDoubleSeries(BoliLower.Sender, TAlphaColors.Gray);
|
||||
Panel.AddDoubleSeries(BoliLower.Sender, TAlphaColors.Gray);
|
||||
|
||||
chart.AddPanel;
|
||||
Panel := chart.AddPanel;
|
||||
|
||||
// Add RSI (Relative Strength Index)
|
||||
var Rsi: IMycConverter<Double, Double> := TGenericIndicator<Double, Double>.Create(TIndicators.CreateRSI(14));
|
||||
Closes.Sender.Link(Rsi);
|
||||
chart.Panels[1].AddDoubleSeries(Rsi.Sender, TAlphaColors.Fuchsia);
|
||||
{
|
||||
Panel.AddDoubleSeries(Rsi.Sender, TAlphaColors.Fuchsia);
|
||||
|
||||
// Add MACD (12, 26, 9)
|
||||
var Macd: IMycConverter<Double, TMacdResult> := TGenericIndicator<Double, TMacdResult>.Create(TIndicators.CreateMACD(12, 26, 9));
|
||||
Closes.Sender.Link(Macd);
|
||||
|
||||
var MacdLine: IMycConverter<TMacdResult, Double> := TMycGenericConverter<TMacdResult, Double>.Create(function(const Item: TMacdResult): Double begin Result := Item.MacdLine; end);
|
||||
Panel := chart.AddPanel;
|
||||
|
||||
var MacdLine: IMycConverter<TMacdResult, Double> :=
|
||||
TMycGenericConverter<TMacdResult, Double>.Create(function(const Item: TMacdResult): Double begin Result := Item.MacdLine; end);
|
||||
Macd.Sender.Link(MacdLine);
|
||||
chart.AddDoubleSeries(MacdLine.Sender, TAlphaColors.Orange);
|
||||
Panel.AddDoubleSeries(MacdLine.Sender, TAlphaColors.Orange);
|
||||
|
||||
var MacdSignal: IMycConverter<TMacdResult, Double> := TMycGenericConverter<TMacdResult, Double>.Create(function(const Item: TMacdResult): Double begin Result := Item.SignalLine; end);
|
||||
var MacdSignal: IMycConverter<TMacdResult, Double> :=
|
||||
TMycGenericConverter<TMacdResult, Double>.Create(function(const Item: TMacdResult): Double begin Result := Item.SignalLine; end);
|
||||
Macd.Sender.Link(MacdSignal);
|
||||
chart.AddDoubleSeries(MacdSignal.Sender, TAlphaColors.Dodgerblue);
|
||||
Panel.AddDoubleSeries(MacdSignal.Sender, TAlphaColors.Dodgerblue);
|
||||
|
||||
var MacdHist: IMycConverter<TMacdResult, Double> := TMycGenericConverter<TMacdResult, Double>.Create(function(const Item: TMacdResult): Double begin Result := Item.Histogram; end);
|
||||
var MacdHist: IMycConverter<TMacdResult, Double> :=
|
||||
TMycGenericConverter<TMacdResult, Double>.Create(function(const Item: TMacdResult): Double begin Result := Item.Histogram; end);
|
||||
Macd.Sender.Link(MacdHist);
|
||||
chart.AddDoubleSeries(MacdHist.Sender, TAlphaColors.Lightgreen, 1.0);
|
||||
Panel.AddDoubleSeries(MacdHist.Sender, TAlphaColors.Lightgreen, 1.0);
|
||||
|
||||
Panel := chart.AddPanel;
|
||||
|
||||
// Add Stochastic Oscillator (14, 3) - This needs OHLC data, not just Close prices.
|
||||
var Stoch: IMycConverter<TOhlcItem, TStochasticResult> := TGenericIndicator<TOhlcItem, TStochasticResult>.Create(TIndicators.CreateStochastic(14, 3));
|
||||
var Stoch: IMycConverter<TOhlcItem, TStochasticResult> :=
|
||||
TGenericIndicator<TOhlcItem, TStochasticResult>.Create(TIndicators.CreateStochastic(14, 3));
|
||||
Ohlc.Sender.Link(Stoch);
|
||||
|
||||
var StochK: IMycConverter<TStochasticResult, Double> := TMycGenericConverter<TStochasticResult, Double>.Create(function(const Item: TStochasticResult): Double begin Result := Item.K; end);
|
||||
var StochK: IMycConverter<TStochasticResult, Double> :=
|
||||
TMycGenericConverter<TStochasticResult, Double>.Create(function(const Item: TStochasticResult): Double begin Result := Item.K; end);
|
||||
Stoch.Sender.Link(StochK);
|
||||
chart.AddDoubleSeries(StochK.Sender, TAlphaColors.Green);
|
||||
Panel.AddDoubleSeries(StochK.Sender, TAlphaColors.Green);
|
||||
|
||||
var StochD: IMycConverter<TStochasticResult, Double> := TMycGenericConverter<TStochasticResult, Double>.Create(function(const Item: TStochasticResult): Double begin Result := Item.D; end);
|
||||
var StochD: IMycConverter<TStochasticResult, Double> :=
|
||||
TMycGenericConverter<TStochasticResult, Double>.Create(function(const Item: TStochasticResult): Double begin Result := Item.D; end);
|
||||
Stoch.Sender.Link(StochD);
|
||||
chart.AddDoubleSeries(StochD.Sender, TAlphaColors.Red);
|
||||
}
|
||||
OhlcPoint.Sender.Link(TimeStamps);
|
||||
|
||||
chart.SetXAxisSeries<TDateTime>(Timestamps.Sender);
|
||||
chart.Panels[0].AddOhlcSeries(Ohlc.Sender);
|
||||
Panel.AddDoubleSeries(StochD.Sender, TAlphaColors.Red);
|
||||
|
||||
var done := ExecuteStrategy(Symbol, OhlcPoint);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user