Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/FFT.Oanda/Accounts/AccountFinancingMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ public enum AccountFinancingMode
/// Account daily at 5pm New York time.
/// </summary>
DAILY,

/// <summary>
/// Not in the Oanda API documentation but in my transaction history
/// </summary>
DAILY_FINANCING,
DAILY_INSTRUMENT,
SECOND_BY_SECOND_COMPONENT,

}
2 changes: 1 addition & 1 deletion src/FFT.Oanda/Accounts/AccountSummary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public record AccountSummary
/// <summary>
/// The date/time that the account’s resettablePL was last reset.
/// </summary>
[JsonConverter(typeof(ResettablePLDateConverter))]
// [JsonConverter(typeof(ResettablePLDateConverter))] blows up the response if you call OandaApiClient.GetAccountSummary, ResettablePLTime comes with default dateTime spec in json data
public DateTime? ResettablePLTime { get; init; }

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/FFT.Oanda/Instruments/Candlestick.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public sealed record CandleStick
/// The number of prices created during the time-range represented by the
/// candlestick.
/// </summary>
public double Volume { get; init; }
public decimal Volume { get; init; } // no reason to use double

/// <summary>
/// A flag indicating if the candlestick is complete. A complete candlestick
Expand Down
8 changes: 4 additions & 4 deletions src/FFT.Oanda/Instruments/CandlestickData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ public sealed record CandlestickData
/// The first (open) price in the time-range represented by the candlestick.
/// </summary>
[JsonPropertyName("o")]
public required double Open { get; init; }
public required decimal Open { get; init; }

/// <summary>
/// The highest price in the time-range represented by the candlestick.
/// </summary>
[JsonPropertyName("h")]
public required double High { get; init; }
public required decimal High { get; init; }

/// <summary>
/// The lowest price in the time-range represented by the candlestick.
/// </summary>
[JsonPropertyName("l")]
public required double Low { get; init; }
public required decimal Low { get; init; }

/// <summary>
/// A flag indicating if the candlestick is complete. A complete candlestick
/// is one whose ending time is not in the future.
/// </summary>
[JsonPropertyName("c")]
public required double Close { get; init; }
public required decimal Close { get; init; }
}
48 changes: 39 additions & 9 deletions src/FFT.Oanda/OandaApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1277,7 +1277,10 @@ public IAsyncEnumerable<Transaction> GetTransactionStreamSince(string accountId,

// Immediately calling MoveNextAsync is required to get the stream to actually connect and start receiving messages.
await using var liveStream = GetTransactionsStream(accountId, cts.Token).GetAsyncEnumerator(cts.Token);
var liveStreamMoveNext = liveStream.MoveNextAsync(cts.Token);

// var liveStreamMoveNext = liveStream.MoveNextAsync(cts.Token);
var liveStreamMoveNext = liveStream.MoveNextAsync(); // at least in Net8 the call is parameterless

await Task.Delay(1000, cts.Token); // Give the live stream time to establish before downloading transactions from the past.

var rangeReponse = await GetTransactionIdRange(accountId, from, null, cts.Token);
Expand All @@ -1294,13 +1297,18 @@ public IAsyncEnumerable<Transaction> GetTransactionStreamSince(string accountId,
}

while (await liveStreamMoveNext && liveStream.Current.Id <= lastTransactionId)
liveStreamMoveNext = liveStream.MoveNextAsync(cts.Token);
{
// liveStreamMoveNext = liveStream.MoveNextAsync(cts.Token);
liveStreamMoveNext = liveStream.MoveNextAsync(); // at least in Net8 the call is parameterless
}
}

while (await liveStreamMoveNext)
{
await result.Writer.WriteAsync(liveStream.Current, cts.Token); // TODO: Throw exception if writing is blocked a long time due to slow reading by consumer.
liveStreamMoveNext = liveStream.MoveNextAsync(cts.Token);

// liveStreamMoveNext = liveStream.MoveNextAsync(cts.Token);
liveStreamMoveNext = liveStream.MoveNextAsync(); // at least in Net8 the call is parameterless
}

result.Writer.Complete();
Expand Down Expand Up @@ -1574,19 +1582,35 @@ public async Task<CandlestickResponse> GetCandlestickData(
int dailyAlignment = 17,
string alignmentTimezone = "America/New_York",
WeeklyAlignment weeklyAlignment = WeeklyAlignment.FRIDAY,
int units = 1,
// int units = 1, // not supported by the API
CancellationToken cancellationToken = default)
{
candleSpecification.ThrowIfNull().Value.Validate();
count.Throw().IfOutOfRange(500, 5000);
to?.Throw().IfDateTimeKindNot(DateTimeKind.Utc);
from?.Throw().IfDateTimeKindNot(DateTimeKind.Utc);
(includeFirst.HasValue == from.HasValue).Throw().IfFalse(); // includeFirst is required with from, and must not be set if from is not set.
(from.HasValue && to.HasValue).Throw().IfTrue();

// includeFirst is required with from, and must not be set if from is not set.
// (includeFirst.HasValue == from.HasValue).Throw().IfFalse();
// instead of throwing an exception better to use default
// else you always have to set the parameters bewteen from and includeFirst
if (from != null && from.HasValue)
{
if (includeFirst == null || !includeFirst.HasValue)
{
includeFirst = true;
}
}

// (from.HasValue && to.HasValue).Throw().IfTrue();
// why cut down the API features?
// it is usefull to have the option to get a specific area of candles between two timestamps
// at least I use that feature

dailyAlignment.Throw().IfOutOfRange(0, 23);
alignmentTimezone.ThrowIfNull().Throw().IfWhiteSpace();
weeklyAlignment.Throw().IfOutOfRange();
units.Throw().IfLessThan(1);
// units.Throw().IfLessThan(1); // Not supported by the API

var query = new Dictionary<string, string>
{
Expand All @@ -1596,10 +1620,16 @@ public async Task<CandlestickResponse> GetCandlestickData(
{ "dailyAlignment", dailyAlignment.ToString(InvariantCulture) },
{ "alignmentTimezone", alignmentTimezone },
{ "weeklyAlignment", weeklyAlignment.ToString() },
{ "units", units.ToString(InvariantCulture) },
{ "count", count.ToString(InvariantCulture) },
// { "units", units.ToString(InvariantCulture) }, // is not supported by the API
// { "count", count.ToString(InvariantCulture) }, // should not be send in any case
};

// count is not allowed if both of 'to' and 'from' are set
if ((to == null) || (from == null))
{
query.Add("count", count.ToString(InvariantCulture));
}

if (to.HasValue) query.Add("to", to.Value.ToString(DATETIMEFORMATSTRING, InvariantCulture));
if (from.HasValue) query.Add("from", from.Value.ToString(DATETIMEFORMATSTRING, InvariantCulture));
if (includeFirst.HasValue) query.Add("includeFirst", includeFirst.Value.ToString());
Expand Down
Loading