From 6af567b2c5403db918e1206a4afacfdeb65a4424 Mon Sep 17 00:00:00 2001 From: AKosylo Date: Tue, 10 Oct 2023 14:47:12 +0200 Subject: [PATCH] =?UTF-8?q?IPF=20Connect=20CL:=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DXFeedFramework.xcodeproj/project.pbxproj | 4 + .../xcshareddata/xcschemes/Tools.xcscheme | 6 +- DXFeedFramework/Events/Market/Quote.swift | 2 +- .../Events/Market/TimeAndSale.swift | 2 +- DXFeedFramework/Utils/TimeUtil.swift | 10 ++- Samples/PerfTestCL/IpfConnectCommand.swift | 79 +++++++++++++++++++ Samples/PerfTestCL/main.swift | 1 + Samples/Utils/ArgumentParser.swift | 2 +- 8 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 Samples/PerfTestCL/IpfConnectCommand.swift diff --git a/DXFeedFramework.xcodeproj/project.pbxproj b/DXFeedFramework.xcodeproj/project.pbxproj index 4eb4528ff..4e193d76d 100644 --- a/DXFeedFramework.xcodeproj/project.pbxproj +++ b/DXFeedFramework.xcodeproj/project.pbxproj @@ -198,6 +198,7 @@ 648E98AA2AAF625800BFD219 /* IIndexedEvent+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648E98A92AAF625800BFD219 /* IIndexedEvent+Ext.swift */; }; 649282E72AD54919008F0F04 /* ScheduleUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649282E62AD54919008F0F04 /* ScheduleUtils.swift */; }; 649282E82AD54919008F0F04 /* ScheduleUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649282E62AD54919008F0F04 /* ScheduleUtils.swift */; }; + 649282EB2AD55323008F0F04 /* IpfConnectCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649282E92AD55323008F0F04 /* IpfConnectCommand.swift */; }; 64963B6A2A8E545C001E40F7 /* IEventType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64963B692A8E545C001E40F7 /* IEventType.swift */; }; 6498E6B22AB1D41A0093A065 /* DXSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6498E6B12AB1D41A0093A065 /* DXSchedule.swift */; }; 6498E6B52AB1D4480093A065 /* NativeSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6498E6B42AB1D4480093A065 /* NativeSchedule.swift */; }; @@ -620,6 +621,7 @@ 648BD5702AC583AC004A3A95 /* TimeFormat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeFormat.swift; sourceTree = ""; }; 648E98A92AAF625800BFD219 /* IIndexedEvent+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IIndexedEvent+Ext.swift"; sourceTree = ""; }; 649282E62AD54919008F0F04 /* ScheduleUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleUtils.swift; sourceTree = ""; }; + 649282E92AD55323008F0F04 /* IpfConnectCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IpfConnectCommand.swift; sourceTree = ""; }; 64963B692A8E545C001E40F7 /* IEventType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IEventType.swift; sourceTree = ""; }; 6498E6B12AB1D41A0093A065 /* DXSchedule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DXSchedule.swift; sourceTree = ""; }; 6498E6B42AB1D4480093A065 /* NativeSchedule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeSchedule.swift; sourceTree = ""; }; @@ -860,6 +862,7 @@ 648BD5682AC450D6004A3A95 /* ConnectCommand.swift */, 6486B97E2AD4167800D8D5FA /* LiveIpfCommand.swift */, 64FFE59E2AD430E4003D3353 /* ScheduleCommand.swift */, + 649282E92AD55323008F0F04 /* IpfConnectCommand.swift */, 648BD56A2AC4576F004A3A95 /* HelpCommand.swift */, 648BD56C2AC56A04004A3A95 /* SubscriptionUtils.swift */, ); @@ -1858,6 +1861,7 @@ 647426AF2ABC93900012F793 /* EventCode+String.swift in Sources */, 645BE8542AC3229D0028243D /* ToolsCommand.swift in Sources */, 644FE5D12AC1F34000580E3A /* LatencyMetricsPrinter.swift in Sources */, + 649282EB2AD55323008F0F04 /* IpfConnectCommand.swift in Sources */, 6486B97F2AD4167800D8D5FA /* LiveIpfCommand.swift in Sources */, 645BE8522AC31E7C0028243D /* PerfTestCommand.swift in Sources */, 641BDD5D2ACD67A000236B78 /* LatencyListener.swift in Sources */, diff --git a/DXFeedFramework.xcodeproj/xcshareddata/xcschemes/Tools.xcscheme b/DXFeedFramework.xcodeproj/xcshareddata/xcschemes/Tools.xcscheme index b605bb8de..8aad9cd05 100644 --- a/DXFeedFramework.xcodeproj/xcshareddata/xcschemes/Tools.xcscheme +++ b/DXFeedFramework.xcodeproj/xcshareddata/xcschemes/Tools.xcscheme @@ -56,9 +56,13 @@ isEnabled = "NO"> + + diff --git a/DXFeedFramework/Events/Market/Quote.swift b/DXFeedFramework/Events/Market/Quote.swift index 7f122048e..07d4b139b 100644 --- a/DXFeedFramework/Events/Market/Quote.swift +++ b/DXFeedFramework/Events/Market/Quote.swift @@ -129,7 +129,7 @@ extension Quote { func baseFieldsToString() -> String { return """ \(eventSymbol), \ -eventTime=" + \(TimeUtil.toLocalDateString(millis: eventTime)), \ +eventTime=\(TimeUtil.toLocalDateString(millis: eventTime)), \ time=\(TimeUtil.toLocalDateString(millis: time)), \ timeNanoPart=\(timeNanoPart), \ sequence=\(getSequence()), \ diff --git a/DXFeedFramework/Events/Market/TimeAndSale.swift b/DXFeedFramework/Events/Market/TimeAndSale.swift index f665319b9..6f0da29f7 100644 --- a/DXFeedFramework/Events/Market/TimeAndSale.swift +++ b/DXFeedFramework/Events/Market/TimeAndSale.swift @@ -257,7 +257,7 @@ spread=\(isSpreadLeg), \ ETH=\(isExtendedTradingHours), \ validTick=\(isValidTick), \ type=\(timeAndSaleType)\(buyer == nil ? "" : ", buyer='\(buyer ?? "null")'")\ - \(seller == nil ? "" : ", seller='\(seller ?? "null")'")\ +\(seller == nil ? "" : ", seller='\(seller ?? "null")'")\ } """ } diff --git a/DXFeedFramework/Utils/TimeUtil.swift b/DXFeedFramework/Utils/TimeUtil.swift index fdbf04056..2ca3b4b27 100644 --- a/DXFeedFramework/Utils/TimeUtil.swift +++ b/DXFeedFramework/Utils/TimeUtil.swift @@ -15,12 +15,12 @@ public class TimeUtil { static let dateFormatter = { let formatter = DateFormatter() - formatter.dateFormat = "dd-MM-yyyy HH:mm" + formatter.dateFormat = "yyyyMMdd-HHmmss" return formatter }() static let dateFormatterWithMillis = { let formatter = DateFormatter() - formatter.dateFormat = "dd-MM-yyyy HH:mm.SSS" + formatter.dateFormat = "yyyyMMdd-HHmmss.SSS" return formatter }() @@ -29,11 +29,17 @@ public class TimeUtil { } public static func toLocalDateString(millis: Int64) -> String { + if millis == 0 { + return "0" + } let timeInterval = Double(millis) / 1000 return (dateFormatterWithMillis.string(from: Date(timeIntervalSince1970: timeInterval))) } public static func toLocalDateStringWithoutMillis(millis: Int64) -> String { + if millis == 0 { + return "0" + } let timeInterval = Double(millis) / 1000 return (dateFormatter.string(from: Date(timeIntervalSince1970: timeInterval))) } diff --git a/Samples/PerfTestCL/IpfConnectCommand.swift b/Samples/PerfTestCL/IpfConnectCommand.swift new file mode 100644 index 000000000..ff2aa5b0b --- /dev/null +++ b/Samples/PerfTestCL/IpfConnectCommand.swift @@ -0,0 +1,79 @@ +// +// IpfConnectCommand.swift +// DXFeedFramework +// +// Created by Aleksey Kosylo on 10.10.23. +// + +import Foundation +import DXFeedFramework + +class IpfConnectCommand: ToolsCommand { + lazy var name = { + stringReference(self) + }() + var cmd = "DXFeedIpfConnect" + + var shortDescription = "A sample program that demonstrates IPF Connection" + + var fullDescription: String = + """ + A sample program that demonstrates IPF Connection + + usage: DXFeedIpfConnect + Where: + is dxfeed event type (" + eventTypeNames + ")" + is name of instrument profiles file + + sample: DXFeedIpfConnect TimeAndSale sample.ipf.zip + """ + + func execute() { + var arguments: [String]! + do { + arguments = try ArgumentParser().parse(ProcessInfo.processInfo.arguments, requiredNumberOfArguments: 2) + } catch { + print(fullDescription) + } + let eventType = arguments[1] + let ipfFile = arguments[2] + try? SystemProperty.setProperty(DXEndpoint.Property.address.rawValue, "demo.dxfeed.com:7300") + let subscription = try? DXEndpoint.getInstance().getFeed()?.createSubscription([EventCode(string: eventType)]) + try? subscription?.add(observer: self) + let symbols = getSymbols(fileName: ipfFile) + try? subscription?.addSymbols(symbols) + // Print till input new line + _ = readLine() + } + + private func getSymbols(fileName: String) -> [String] { + print("Reading instruments from \(fileName)") + let profiles = try? DXInstrumentProfileReader().readFromFile(address: fileName) + print("Selected symbols are:") + let result = profiles?.compactMap({ profile in + if profile.type == "STOCK" { + print("\(profile.symbol) (\(profile.descriptionStr))") + return profile.symbol + } else { + return nil + } + }) + return result ?? [String]() + } +} +extension IpfConnectCommand: Hashable { + static func == (lhs: IpfConnectCommand, rhs: IpfConnectCommand) -> Bool { + return lhs === rhs || lhs.name == rhs.name + } + + func hash(into hasher: inout Hasher) { + hasher.combine(name) + } +} +extension IpfConnectCommand: DXEventListener { + func receiveEvents(_ events: [DXFeedFramework.MarketEvent]) { + events.forEach { mEvent in + print("\(mEvent.eventSymbol): \(mEvent.quote.toString())") + } + } +} diff --git a/Samples/PerfTestCL/main.swift b/Samples/PerfTestCL/main.swift index 67e55bca8..287d1288e 100644 --- a/Samples/PerfTestCL/main.swift +++ b/Samples/PerfTestCL/main.swift @@ -12,6 +12,7 @@ let commands: [ToolsCommand] = [PerfTestCommand(), LatencyTestCommand(), LiveIpfCommand(), ScheduleCommand(), + IpfConnectCommand(), HelpCommand()] func getCommand(_ cmd: String) -> ToolsCommand? { diff --git a/Samples/Utils/ArgumentParser.swift b/Samples/Utils/ArgumentParser.swift index 152ec9796..8ad68a7a8 100644 --- a/Samples/Utils/ArgumentParser.swift +++ b/Samples/Utils/ArgumentParser.swift @@ -22,7 +22,7 @@ class ArgumentParser { Cmd \(cmd) contains not enough \(cmd.count - 1) arguments. Expected \(requiredNumberOfArguments) """) } - + // 0 Arg is path to executed app return Array(cmd[1..