Skip to content

Commit

Permalink
Schedule:
Browse files Browse the repository at this point in the history
next/prev day,
next/prev session
get session by time
  • Loading branch information
kosyloa committed Sep 19, 2023
1 parent 8bfbc95 commit 222c677
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 60 deletions.
4 changes: 4 additions & 0 deletions DXFeedFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
64B436402AB8857F0003919E /* SessionFilter+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B4363F2AB8857F0003919E /* SessionFilter+Ext.swift */; };
64B436422AB88DE70003919E /* NativeSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B436412AB88DE70003919E /* NativeSession.swift */; };
64B436442AB88EA40003919E /* NativeDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B436432AB88EA40003919E /* NativeDay.swift */; };
64B436462AB985AE0003919E /* NativeBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B436452AB985AE0003919E /* NativeBox.swift */; };
64B627352A375C0F00196D07 /* QuoteModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B627292A375C0E00196D07 /* QuoteModel.swift */; };
64B627382A375C0F00196D07 /* QuoteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B6272C2A375C0E00196D07 /* QuoteViewController.swift */; };
64B627392A375C0F00196D07 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B6272D2A375C0E00196D07 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -546,6 +547,7 @@
64B4363F2AB8857F0003919E /* SessionFilter+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionFilter+Ext.swift"; sourceTree = "<group>"; };
64B436412AB88DE70003919E /* NativeSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeSession.swift; sourceTree = "<group>"; };
64B436432AB88EA40003919E /* NativeDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeDay.swift; sourceTree = "<group>"; };
64B436452AB985AE0003919E /* NativeBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeBox.swift; sourceTree = "<group>"; };
64B627152A375BBA00196D07 /* DXQuoteTableApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DXQuoteTableApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
64B627292A375C0E00196D07 /* QuoteModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuoteModel.swift; sourceTree = "<group>"; };
64B6272C2A375C0E00196D07 /* QuoteViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuoteViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -931,6 +933,7 @@
64B4363F2AB8857F0003919E /* SessionFilter+Ext.swift */,
64B436412AB88DE70003919E /* NativeSession.swift */,
64B436432AB88EA40003919E /* NativeDay.swift */,
64B436452AB985AE0003919E /* NativeBox.swift */,
);
path = Schedule;
sourceTree = "<group>";
Expand Down Expand Up @@ -1728,6 +1731,7 @@
64ECD6852A9DDF6200B36935 /* DXInstrumentProfileCollector.swift in Sources */,
64656F5B2A1B9784006A0B19 /* DXFeed.swift in Sources */,
6447A5EF2A8FD1CD00739CCF /* DayUtil.swift in Sources */,
64B436462AB985AE0003919E /* NativeBox.swift in Sources */,
6447A5E92A8F9E0B00739CCF /* TimeNanosUtil.swift in Sources */,
640C3FD82A617D4900555161 /* CandlePriceLevel.swift in Sources */,
6498E6C42AB20B860093A065 /* ScheduleSessionType+Ext.swift in Sources */,
Expand Down
19 changes: 19 additions & 0 deletions DXFeedFramework/Native/Schedule/NativeBox.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// NativeBox.swift
// DXFeedFramework
//
// Created by Aleksey Kosylo on 19.09.23.
//

import Foundation
@_implementationOnly import graal_api

/// Just box for graal structure.
///
/// Please, override deinit to correct deallocation graal structure
class NativeBox<T> {
let native: UnsafeMutablePointer<T>
init(native: UnsafeMutablePointer<T>) {
self.native = native
}
}
9 changes: 1 addition & 8 deletions DXFeedFramework/Native/Schedule/NativeDay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,9 @@
import Foundation
@_implementationOnly import graal_api

class NativeDay {
let native: UnsafeMutablePointer<dxfg_day_t>

init(native: UnsafeMutablePointer<dxfg_day_t>) {
self.native = native
}

class NativeDay: NativeBox<dxfg_day_t> {
deinit {
let thread = currentThread()
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(native.pointee.handler)))
}

}
83 changes: 51 additions & 32 deletions DXFeedFramework/Native/Schedule/NativeSchedule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,19 @@ class NativeSchedule {
public func getDayByTime(time: Long) throws -> ScheduleDay {
let thread = currentThread()
let day = try ErrorCheck.nativeCall(thread, dxfg_Schedule_getDayByTime(thread, schedule, time))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(day.pointee.handler)))
}
return try createDay(thread, day)
}

public func getDayById(dayId: Int32) throws -> ScheduleDay {
let thread = currentThread()
let day = try ErrorCheck.nativeCall(thread, dxfg_Schedule_getDayById(thread, schedule, dayId))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(day.pointee.handler)))
}
return try createDay(thread, day)
}

private func createDay(_ thread: OpaquePointer?,
_ day: UnsafeMutablePointer<dxfg_day_t>) throws -> ScheduleDay {
let scheduleDay = ScheduleDay()
scheduleDay.native = NativeDay(native: day)
scheduleDay.nativeSchedule = self
scheduleDay.dayId = try ErrorCheck.nativeCall(thread, dxfg_Day_getDayId(thread, day))
scheduleDay.yearMonthDay = try ErrorCheck.nativeCall(thread, dxfg_Day_getYearMonthDay(thread, day))
Expand Down Expand Up @@ -133,60 +128,84 @@ class NativeSchedule {
return session
}

internal func getNextDay(after day: ScheduleDay, filter: DayFilter ) throws -> ScheduleDay? {
let qdValue = filter.toQDValue()
let thread = currentThread()
let filter = try ErrorCheck.nativeCall(thread, dxfg_DayFilter_getInstance(thread, qdValue))
let currentDay = try ErrorCheck.nativeCall(thread, dxfg_Schedule_getDayById(thread, schedule, day.dayId))
let nextDay = try ErrorCheck.nativeCall(thread, dxfg_Day_getNextDay(thread, currentDay, filter))
let day = try? createDay(thread, nextDay)
try ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(nextDay.pointee.handler)))
return day
internal func getNextDay(after day: ScheduleDay, filter: DayFilter) throws -> ScheduleDay? {
try getDay(for: day, filter: filter) { thread, nativeDay, filter in
try ErrorCheck.nativeCall(thread, dxfg_Day_getNextDay(thread, nativeDay, filter))
}
}

internal func getPrevtDay(before day: ScheduleDay, filter: DayFilter ) throws -> ScheduleDay? {
try getDay(for: day, filter: filter) { thread, nativeDay, filter in
try ErrorCheck.nativeCall(thread, dxfg_Day_getPrevDay(thread, nativeDay, filter))
}
}

typealias GetDayExecutor =
(OpaquePointer?,
UnsafeMutablePointer<dxfg_day_t>?,
UnsafeMutablePointer<dxfg_day_filter_t>) throws -> UnsafeMutablePointer<dxfg_day_t>

private func getDay(for day: ScheduleDay,
filter: DayFilter,
executor: GetDayExecutor) throws -> ScheduleDay? {
let qdValue = filter.toQDValue()
let thread = currentThread()
let filter = try ErrorCheck.nativeCall(thread, dxfg_DayFilter_getInstance(thread, qdValue))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(filter.pointee.handler)))
}
let currentDay = try ErrorCheck.nativeCall(thread, dxfg_Schedule_getDayById(thread, schedule, day.dayId))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(currentDay.pointee.handler)))
}
let prevDay = try ErrorCheck.nativeCall(thread, dxfg_Day_getPrevDay(thread, currentDay, filter))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(prevDay.pointee.handler)))
}
let day = try? createDay(thread, prevDay)
let nextDay = try executor(thread, day.native?.native, filter)
let day = try? createDay(thread, nextDay)
return day
}

internal func getNextSession(after session: ScheduleSession, filter: SessionFilter ) throws -> ScheduleSession? {
internal func getNextSession(after session: ScheduleSession,
filter: SessionFilter) throws -> ScheduleSession? {
try getSession(for: session, filter: filter, executor: { thread, session, filter in
try ErrorCheck.nativeCall(thread, dxfg_Session_getNextSession(thread, session, filter))
})
}

internal func getPrevtSession(before session: ScheduleSession, filter: SessionFilter ) throws -> ScheduleSession? {
try getSession(for: session, filter: filter, executor: { thread, session, filter in
try ErrorCheck.nativeCall(thread, dxfg_Session_getPrevSession(thread, session, filter))
})
}

typealias GetSessionyExecutor =
(OpaquePointer?,
UnsafeMutablePointer<dxfg_session_t>?,
UnsafeMutablePointer<dxfg_session_filter_t>) throws -> UnsafeMutablePointer<dxfg_session_t>

private func getSession(for session: ScheduleSession,
filter: SessionFilter,
executor: GetSessionyExecutor) throws -> ScheduleSession? {
let qdValue = filter.toQDValue()
let thread = currentThread()
let filter = try ErrorCheck.nativeCall(thread, dxfg_SessionFilter_getInstance(thread, qdValue))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(filter.pointee.handler)))
}
let currentSession = session.native.native
let nextSession = try ErrorCheck.nativeCall(thread,
dxfg_Session_getNextSession(thread, currentSession, filter))
let nextSession = try executor(thread, session.native.native, filter)
let session = try createSession(thread, session: nextSession)
return session
}

internal func getPrevtSession(before session: ScheduleSession, filter: SessionFilter ) throws -> ScheduleSession? {
public func getSessionByTime(time: Long) throws -> ScheduleSession {
let thread = currentThread()
let nextSession = try ErrorCheck.nativeCall(thread, dxfg_Schedule_getSessionByTime(thread, schedule, time))
let session = try createSession(thread, session: nextSession)
return session
}

public func getNearestSessionByTime(time: Long, filter: SessionFilter) throws -> ScheduleSession {
let qdValue = filter.toQDValue()
let thread = currentThread()
let filter = try ErrorCheck.nativeCall(thread, dxfg_SessionFilter_getInstance(thread, qdValue))
defer {
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(filter.pointee.handler)))
}
let currentSession = session.native.native
let nextSession = try ErrorCheck.nativeCall(thread,
dxfg_Session_getPrevSession(thread, currentSession, filter))
let nextSession = try ErrorCheck.nativeCall(thread, dxfg_Schedule_getNearestSessionByTime(thread, schedule, time, filter))
let session = try createSession(thread, session: nextSession)
return session
}
Expand Down
8 changes: 1 addition & 7 deletions DXFeedFramework/Native/Schedule/NativeSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@
import Foundation
@_implementationOnly import graal_api

class NativeSession {
let native: UnsafeMutablePointer<dxfg_session_t>

init(native: UnsafeMutablePointer<dxfg_session_t>) {
self.native = native
}

class NativeSession: NativeBox<dxfg_session_t> {
deinit {
let thread = currentThread()
_ = try? ErrorCheck.nativeCall(thread, dxfg_JavaObjectHandler_release(thread, &(native.pointee.handler)))
Expand Down
42 changes: 38 additions & 4 deletions DXFeedFramework/Schedule/DXSchedule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,58 @@ public class DXSchedule {

/// Returns day that contains specified time.
///
/// This method will throw {@link IllegalArgumentException} if specified time
/// This method will throw exception if specified time
/// falls outside of valid date range from 0001-01-02 to 9999-12-30.
/// - Parameters:
/// - time: the time to search for
/// - Throws: GraalException. Rethrows exception from Java.recore
public func getDayByTime(time: Long) throws -> ScheduleDay {
var day = try native.getDayByTime(time: time)
let day = try native.getDayByTime(time: time)
return day
}

/// Returns day for specified day identifier.
///
/// This method will throw {@link IllegalArgumentException} if specified day identifier
/// This method will throw exception if specified day identifier
/// falls outside of valid date range from 0001-01-02 to 9999-12-30.
/// - Parameters:
/// - day: identifier to search for
/// - Throws: GraalException. Rethrows exception from Java.recore
public func getDayById(day: Int32) throws -> ScheduleDay {
var day = try native.getDayById(dayId: day)
let day = try native.getDayById(dayId: day)
return day
}

/// Returns session that contains specified time.
///
/// This method will throw exception
/// if specified time falls outside of valid date range from 0001-01-02 to 9999-12-30.
///
/// - Parameters:
/// - time: time the time to search for
/// - Returns: session that contains specified time
/// - Throws: GraalException. Rethrows exception from Java.
public func getSessionByTime(time: Long) throws -> ScheduleSession {
return try native.getSessionByTime(time: time)
}

/// Returns session that is nearest to the specified time and that is accepted by specified filter.
///
/// This method will throw exception if specified time
/// falls outside of valid date range from 0001-01-02 to 9999-12-30.
/// If no sessions acceptable by specified filter are found within one year this method will throw exception
///
/// To find nearest trading session of any type use this code:
/// session = schedule.getNearestSessionByTime(time, SessionFilter.TRADING);
/// To find nearest regular trading session use this code:
/// session = schedule.getNearestSessionByTime(time, SessionFilter.REGULAR);
///
/// - Parameters:
/// - time: time the time to search for
/// - filter: the filter to test sessions
/// - Returns: session that contains specified time
/// - Throws: GraalException. Rethrows exception from Java.
public func getNearestSessionByTime(time: Long, filter: SessionFilter) throws -> ScheduleSession {
return try native.getNearestSessionByTime(time: time, filter: filter)
}
}
8 changes: 8 additions & 0 deletions DXFeedFramework/Schedule/ScheduleDay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import Foundation
/// Such sessions can be of any appropriate type, trading or non-trading.
/// Day may have zero duration as well - e.g. when all time within it is transferred to other days.
public class ScheduleDay {
/// Returns native ref
internal var native: NativeDay?
/// Returns schedule to which this day belongs.
internal var nativeSchedule: NativeSchedule?
/// Number of this day since January 1, 1970 (that day has identifier of 0 and previous days have negative identifiers).
Expand Down Expand Up @@ -74,3 +76,9 @@ extension ScheduleDay {
return try nativeSchedule?.getNextDay(after: self, filter: filter)
}
}

extension ScheduleDay: Equatable {
public static func == (lhs: ScheduleDay, rhs: ScheduleDay) -> Bool {
return lhs === rhs || lhs.dayId == rhs.dayId
}
}
6 changes: 3 additions & 3 deletions DXFeedFramework/Schedule/ScheduleSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ public enum ScheduleSessionType {
/// Each session completely fits inside a certain day. Day may contain sessions with zero duration - e.g. indices
/// that post value once a day. Such sessions can be of any appropriate type, trading or non-trading.
public class ScheduleSession {
/// Returns native ref
internal let native: NativeSession
/// Returns schedule to which this session belongs.
internal let nativeSchedule: NativeSchedule
/// Returns start time of this session (inclusive).
///
/// For normal sessions the start time is less than the end time, for empty sessions they are equal.
public let startTime: Long
/// Returns end time of this session (exclusive).
Expand All @@ -63,7 +66,6 @@ public class ScheduleSession {
}
}


extension ScheduleSession {
public func getPrevious(filter: SessionFilter) throws -> ScheduleSession? {
return try nativeSchedule.getPrevtSession(before: self, filter: filter)
Expand All @@ -79,6 +81,4 @@ extension ScheduleSession: Equatable {
return lhs === rhs ||
(lhs.endTime == rhs.endTime && lhs.startTime == rhs.startTime && lhs.type == rhs.type)
}


}
5 changes: 3 additions & 2 deletions DXFeedFramework/Utils/TimeUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import Foundation

class TimeUtil {
static let second = Long(1000)
static let minute = 60 * Long(1000)
static let day = Long(24 * 60 * 60 * 1000)
static let minute = 60 * second
static let hour = 60 * minute
static let day = 24 * hour

static let dateFormatter = {
let formatter = DateFormatter()
Expand Down
20 changes: 16 additions & 4 deletions DXFeedFrameworkTests/ScheduleTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ final class ScheduleTest: XCTestCase {
let timeZone = try schedule.getTimeZone()
XCTAssert(timeZone == "Greenwich Mean Time")
let day = try schedule.getDayByTime(time: 0)
XCTAssert(day.dayId == 0)
XCTAssert(day.dayId == 0)
} catch {
XCTAssert(false, "Error \(error)")
}
Expand Down Expand Up @@ -169,10 +169,11 @@ final class ScheduleTest: XCTestCase {
var startDay: ScheduleDay? = try schedule?.getDayByTime(time: start)
schedule = nil
var nextDay: ScheduleDay? = try startDay?.getNext(filter: .any)
var prevDay: ScheduleDay? = try startDay?.getNext(filter: .any)
var prevDay: ScheduleDay? = try startDay?.getPrevious(filter: .any)
XCTAssert(prevDay != nextDay)
startDay = nil
nextDay = nil
prevDay = nil
prevDay = nil
// waiting here for an explosion in deinit
let sec = 2
_ = XCTWaiter.wait(for: [expectation(description: "\(sec) seconds waiting")], timeout: TimeInterval(sec))
Expand All @@ -187,14 +188,25 @@ final class ScheduleTest: XCTestCase {
XCTAssert(startDay?.sessions.count ?? 0 >= 3)
do {
let firstSession = startDay?.sessions.first

let nextSession = try firstSession?.getNext(filter: .any)
XCTAssert(nextSession == startDay?.sessions[1])
let lastSession = startDay?.sessions.last
let prevSession = try lastSession?.getPrevious(filter: .any)
let index = (startDay?.sessions.count ?? 0) - 2
XCTAssert(prevSession == startDay?.sessions[index])
}
startDay = nil
// waiting here for an explosion in deinit
let sec = 2
_ = XCTWaiter.wait(for: [expectation(description: "\(sec) seconds waiting")], timeout: TimeInterval(sec))
}

func testNearestSessionByTime() throws {
let schedule = try DXSchedule(scheduleDefinition: "(tz=GMT;0=01000200)")
let value = try schedule.getNearestSessionByTime(time: Int64(Date.now.timeIntervalSince1970) * 1000,
filter: .trading )
let startTime = value.startTime % TimeUtil.day
let equals = TimeUtil.hour == startTime
XCTAssert(equals)
}
}

0 comments on commit 222c677

Please sign in to comment.