Skip to content

Commit

Permalink
Add fetching GetLastEvent(s)
Browse files Browse the repository at this point in the history
  • Loading branch information
kosyloa committed Jan 11, 2024
1 parent cc59286 commit d951c24
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 3 deletions.
4 changes: 4 additions & 0 deletions DXFeedFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
645BE8522AC31E7C0028243D /* PerfTestTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645BE8512AC31E7C0028243D /* PerfTestTool.swift */; };
645BE8542AC3229D0028243D /* ToolsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645BE8532AC3229D0028243D /* ToolsCommand.swift */; };
645CD0042AE145E600F99FCF /* DumpTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645CD0032AE145E600F99FCF /* DumpTool.swift */; };
646064EA2B4D8973009201E2 /* DXLastEventTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646064E92B4D8973009201E2 /* DXLastEventTest.swift */; };
646228512A376B0A0029DC97 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6462284D2A376B0A0029DC97 /* Main.storyboard */; };
646228522A376B0A0029DC97 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6462284F2A376B0A0029DC97 /* LaunchScreen.storyboard */; };
646407492A9DF984006FF769 /* InstrumentProfileMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646407482A9DF984006FF769 /* InstrumentProfileMapper.swift */; };
Expand Down Expand Up @@ -673,6 +674,7 @@
645BE8512AC31E7C0028243D /* PerfTestTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerfTestTool.swift; sourceTree = "<group>"; };
645BE8532AC3229D0028243D /* ToolsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolsCommand.swift; sourceTree = "<group>"; };
645CD0032AE145E600F99FCF /* DumpTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DumpTool.swift; sourceTree = "<group>"; };
646064E92B4D8973009201E2 /* DXLastEventTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DXLastEventTest.swift; sourceTree = "<group>"; };
6462284E2A376B0A0029DC97 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
646228502A376B0A0029DC97 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
646407482A9DF984006FF769 /* InstrumentProfileMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstrumentProfileMapper.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1561,6 +1563,7 @@
648C72482B19CA5A00E2FEF3 /* DXExceptionTest.swift */,
641C64B32B347C430023CFAD /* DXObservableSubscriptionTest.swift */,
6423E4682B457000006B208D /* DXTimeSeriesSubscriptionTest.swift */,
646064E92B4D8973009201E2 /* DXLastEventTest.swift */,
);
path = DXFeedFrameworkTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -2485,6 +2488,7 @@
6423E4692B457000006B208D /* DXTimeSeriesSubscriptionTest.swift in Sources */,
64ECD67F2A9CF4CB00B36935 /* IPFTests.swift in Sources */,
64ACBCD52A2789EF00032C53 /* TestListener.swift in Sources */,
646064EA2B4D8973009201E2 /* DXLastEventTest.swift in Sources */,
64098F672ACEB6F70020D741 /* DXConnectionStateTests.swift in Sources */,
648BD56F2AC582AB004A3A95 /* DateTimeParserTest.swift in Sources */,
64ACBCEA2A28DDDA00032C53 /* TestEventListener.swift in Sources */,
Expand Down
10 changes: 10 additions & 0 deletions DXFeedFramework/Api/DXFeed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,13 @@ public class DXFeed {
types: [type])
}
}

public extension DXFeed {
func getLastEvent(type: MarketEvent) throws -> ILastingEvent? {
return try native.getLastEvent(type: type)
}

func getLastEvents(types: [MarketEvent]) throws -> [ILastingEvent] {
return try native.getLastEvents(types: types)
}
}
25 changes: 25 additions & 0 deletions DXFeedFramework/Events/Market/Extensions/MarketEvent+Access.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,29 @@ extension MarketEvent {
public var optionSale: OptionSale {
return (self as? OptionSale)!
}
/// Use only for event.type which supported ``ILastingEvent``
public var lastingEvent: ILastingEvent? {
switch self.type {
case .quote:
return self.quote as ILastingEvent
case .profile:
return self.profile as ILastingEvent
case .summary:
return self.summary as ILastingEvent
case .greeks:
return self.greeks as ILastingEvent
case .candle:
return self.candle as ILastingEvent
case .underlying:
return self.underlying as ILastingEvent
case .theoPrice:
return self.theoPrice as ILastingEvent
case .trade:
return self.trade as ILastingEvent
case .tradeETH:
return self.tradeETH as ILastingEvent
default:
return nil
}
}
}
74 changes: 74 additions & 0 deletions DXFeedFramework/Native/Feed/NativeFeed.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import Foundation
/// The location of the imported functions is in the header files "dxfg_feed.h".
class NativeFeed {
let feed: UnsafeMutablePointer<dxfg_feed_t>?
private let mapper = EventMapper()

deinit {
if let feed = feed {
let thread = currentThread()
Expand Down Expand Up @@ -74,4 +76,76 @@ class NativeFeed {
event.type.nativeCode()))
return NativeTimeSeriesSubscription(native: subscription)
}

func getLastEvent(type: MarketEvent) throws -> ILastingEvent? {
let thread = currentThread()
let inputEvent = try ErrorCheck.nativeCall(thread,
dxfg_EventType_new(
thread,
type.eventSymbol,
type.type.nativeCode())
)
defer {
_ = try? ErrorCheck.nativeCall(thread,
dxfg_EventType_release(
thread,
inputEvent))
}
_ = try ErrorCheck.nativeCall(thread,
dxfg_DXFeed_getLastEvent(
thread,
self.feed,
inputEvent))

let event = try mapper.fromNative(native: inputEvent)
return event?.lastingEvent
}

func getLastEvents(types: [MarketEvent]) throws -> [ILastingEvent] {
let listPointer = UnsafeMutablePointer<dxfg_event_type_list>.allocate(capacity: 1)
listPointer.pointee.size = Int32(types.count)
let classes = UnsafeMutablePointer<UnsafeMutablePointer<dxfg_event_type_t>?>
.allocate(capacity: types.count)
var iterator = classes
let thread = currentThread()

types.forEach { event in
if let inputEvent = try? ErrorCheck.nativeCall(thread, dxfg_EventType_new(
thread,
event.eventSymbol,
event.type.nativeCode())) {
iterator.initialize(to: inputEvent)
iterator = iterator.successor()
}
}
listPointer.pointee.elements = classes

defer {
for index in 0..<Int(listPointer.pointee.size) {
let element = listPointer.pointee.elements[index]
_ = try? ErrorCheck.nativeCall(thread,
dxfg_EventType_release(
thread,
element))
}
listPointer.deinitialize(count: 1)
listPointer.deallocate()
}
var results = [ILastingEvent]()
_ = try ErrorCheck.nativeCall(thread,
dxfg_DXFeed_getLastEvents(
thread,
self.feed,
listPointer))

for index in 0..<Int(listPointer.pointee.size) {
if let elemenent = listPointer.pointee.elements[index] {
let event = try mapper.fromNative(native: elemenent)
if let lastingEvent = event?.lastingEvent {
results.append(lastingEvent)
}
}
}
return results
}
}
153 changes: 153 additions & 0 deletions DXFeedFrameworkTests/DXLastEventTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//
//
// Copyright (C) 2024 Devexperts LLC. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

import XCTest
@testable import DXFeedFramework

final class DXLastEventTest: XCTestCase {
private func testEvents(_ symbol: String) -> [MarketEvent] {

let testQuote = Quote(symbol)
testQuote.bidSize = Double.random(in: 1000.0..<2000.0)
testQuote.askPrice = Double.random(in: 1000.0..<2000.0)
let testTrade = Trade(symbol)
testTrade.price = Double.random(in: 1000.0..<2000.0)
let testTradeEth = TradeETH(symbol)
testTradeEth.price = Double.random(in: 1000.0..<2000.0)
let testProfile = Profile(symbol)
testProfile.highLimitPrice = Double.random(in: 1000.0..<2000.0)

let summary = Summary(symbol)
summary.dayId = 10
var result = [testQuote, testTrade, testTradeEth, testProfile, summary]
if let candleSymbol = try? CandleSymbol.valueOf(symbol) {
let candle = Candle(candleSymbol)
result.append(candle)
}
let greeks = Greeks(symbol)
greeks.price = Double.random(in: 1000.0..<2000.0)
result.append(greeks)

let underlying = Underlying(symbol)
underlying.volatility = Double.random(in: 1000.0..<2000.0)
result.append(underlying)

let theoPrice = TheoPrice(symbol)
theoPrice.delta = Double.random(in: 1000.0..<2000.0)
result.append(theoPrice)

return result
}

private func checkEvent(_ event: ILastingEvent, in events: [MarketEvent]) {
func getEvent(type: EventCode) -> MarketEvent {
events.first { mEvent in
mEvent.type == type
}!
}

switch event {
case let last as Quote:
let test = getEvent(type: last.type).quote
XCTAssert((last.askPrice ~== test.askPrice) &&
(last.bidSize ~== test.bidSize))
case let last as Trade:
let test = getEvent(type: last.type).trade
XCTAssert(test.price ~== last.price)
case let last as TradeETH:
let test = getEvent(type: last.type).tradeETH
XCTAssert(test.price ~== last.price)
case let last as Profile:
let test = getEvent(type: last.type).profile
XCTAssert(last.highLimitPrice ~== test.highLimitPrice)
case let last as Candle:
let test = getEvent(type: last.type).candle
XCTAssert(last.eventSymbol == test.eventSymbol)
case let last as Summary:
let test = getEvent(type: last.type).summary
XCTAssert(last.dayId == test.dayId)
case let last as Greeks:
let test = getEvent(type: last.type).greeks
XCTAssert(last.price ~== test.price)
case let last as Underlying:
let test = getEvent(type: last.type).underlying
XCTAssert(last.volatility ~== test.volatility)
case let last as TheoPrice:
let test = getEvent(type: last.type).theoPrice
XCTAssert(last.delta ~== test.delta)
default:
XCTAssert(false, "Unhandled last event \(event)")
}
}

func testGetLastEvents() {
let symbol = "AAPL_TEST"

let events = testEvents(symbol)
getLastEvents(symbol: symbol, events: events) { feed in
let result = try? feed?.getLastEvents(types: events)
result?.forEach({ event in
checkEvent(event, in: events)
})

events.forEach { event in
if let lastEvent = try? feed?.getLastEvent(type: event) {
checkEvent(lastEvent, in: events)
} else {
XCTAssert(false, "LastEvent returns nil")
}
}
}
}

func getLastEvents(symbol: String, events: [MarketEvent], fetching: ((DXFeed?) -> Void)) {
let port = Int.random(in: 7500..<7600)
do {
let endpoint: DXEndpoint? = try DXEndpoint.create(.publisher)
try endpoint?.connect(":\(port)")
let publisher = endpoint?.getPublisher()
let connectedExpectation = expectation(description: "Connected")
let stateListener: TestEndpoointStateListener? = TestEndpoointStateListener { listener in
listener.callback = { state in
if state == .connected {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 0.3) {
print(Thread.current.threadName)
try? publisher?.publish(events: events)
connectedExpectation.fulfill()
}
}
}
return listener
}
let feedEndpoint = try DXEndpoint.create(.feed)
feedEndpoint.add(listener: stateListener!)
let allTypes = [Candle.self,
Trade.self,
TradeETH.self,
Quote.self,
TimeAndSale.self,
Profile.self,
Summary.self,
Greeks.self,
Underlying.self,
TheoPrice.self,
Order.self,
AnalyticOrder.self,
SpreadOrder.self,
Series.self,
OptionSale.self]
let subscription = try feedEndpoint.getFeed()?.createSubscription(allTypes)
try feedEndpoint.connect("localhost:\(port)")
try subscription?.addSymbols(symbol)
wait(for: [connectedExpectation], timeout: 1)
fetching(feedEndpoint.getFeed())
} catch {
XCTAssert(false, "\(error)")
}
}

}
1 change: 0 additions & 1 deletion DXFeedFrameworkTests/EndpointTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ final class EndpointTest: XCTestCase {
testGetInstance(role: .publisher, count: 150)
}


func testGetEventTypes() throws {
let endpoint = try DXEndpoint.create().connect("demo.dxfeed.com:7300")
let feed = endpoint.getFeed()
Expand Down
4 changes: 2 additions & 2 deletions Samples/Playgrounds/DXFeedconnect.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ let allTypes = [Candle.self,
OptionSale.self]

let argSymbols = ["ETH/USD:GDAX"]
let argTime: String? = nil
let argTime: String? = nil

// To avoid release inside internal {} scope
let feed = try DXEndpoint.getInstance().connect("demo.dxfeed.com:7300").getFeed()
var feedSubscription: DXFeedSubscription? = nil
var feedSubscription: DXFeedSubscription?
let listener = Listener { listener in
listener.callback = { events in
events.forEach { event in
Expand Down

0 comments on commit d951c24

Please sign in to comment.