diff --git a/DXFeedFramework.xcodeproj/project.pbxproj b/DXFeedFramework.xcodeproj/project.pbxproj index 665054d01..8105d68aa 100644 --- a/DXFeedFramework.xcodeproj/project.pbxproj +++ b/DXFeedFramework.xcodeproj/project.pbxproj @@ -276,6 +276,10 @@ 64BA92672A306E3100BE26A0 /* Trade+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BA92662A306E3100BE26A0 /* Trade+Ext.swift */; }; 64BA92692A306E6000BE26A0 /* TradeBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BA92682A306E6000BE26A0 /* TradeBase.swift */; }; 64BA926B2A3072CA00BE26A0 /* TradeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BA926A2A3072CA00BE26A0 /* TradeMapper.swift */; }; + 64BDDB1E2AD6CC6A00694210 /* Order.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BDDB1D2AD6CC6A00694210 /* Order.swift */; }; + 64BDDB202AD6CC8300694210 /* AnalyticOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BDDB1F2AD6CC8300694210 /* AnalyticOrder.swift */; }; + 64BDDB222AD6CC9A00694210 /* SpreadOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BDDB212AD6CC9A00694210 /* SpreadOrder.swift */; }; + 64BDDB242AD6F10200694210 /* Scope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64BDDB232AD6F10200694210 /* Scope.swift */; }; 64C771F22A94A224009868C2 /* Character+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C771F12A94A224009868C2 /* Character+Ext.swift */; }; 64C771F42A94A86E009868C2 /* Side.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C771F32A94A86E009868C2 /* Side.swift */; }; 64C771F62A94ADDA009868C2 /* Direction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C771F52A94ADDA009868C2 /* Direction.swift */; }; @@ -715,6 +719,10 @@ 64BA92662A306E3100BE26A0 /* Trade+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Trade+Ext.swift"; sourceTree = ""; }; 64BA92682A306E6000BE26A0 /* TradeBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TradeBase.swift; sourceTree = ""; }; 64BA926A2A3072CA00BE26A0 /* TradeMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TradeMapper.swift; sourceTree = ""; }; + 64BDDB1D2AD6CC6A00694210 /* Order.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Order.swift; sourceTree = ""; }; + 64BDDB1F2AD6CC8300694210 /* AnalyticOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticOrder.swift; sourceTree = ""; }; + 64BDDB212AD6CC9A00694210 /* SpreadOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpreadOrder.swift; sourceTree = ""; }; + 64BDDB232AD6F10200694210 /* Scope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scope.swift; sourceTree = ""; }; 64C771F12A94A224009868C2 /* Character+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Character+Ext.swift"; sourceTree = ""; }; 64C771F32A94A86E009868C2 /* Side.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Side.swift; sourceTree = ""; }; 64C771F52A94ADDA009868C2 /* Direction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Direction.swift; sourceTree = ""; }; @@ -974,6 +982,9 @@ 6486B9702AD0445E00D8D5FA /* UnderlyingMapper.swift */, 6486B9742AD0493F00D8D5FA /* TheoPrice+Ext.swift */, 6486B9762AD04C5800D8D5FA /* TheoPriceMapper.swift */, + 64BDDB1D2AD6CC6A00694210 /* Order.swift */, + 64BDDB1F2AD6CC8300694210 /* AnalyticOrder.swift */, + 64BDDB212AD6CC9A00694210 /* SpreadOrder.swift */, ); path = Markets; sourceTree = ""; @@ -1121,6 +1132,7 @@ 6486B9782AD04F4000D8D5FA /* OrderBase.swift */, 6486B97A2AD0517A00D8D5FA /* OrderAction.swift */, 6486B97C2AD057F200D8D5FA /* OrderSource.swift */, + 64BDDB232AD6F10200694210 /* Scope.swift */, ); path = Extra; sourceTree = ""; @@ -2132,6 +2144,7 @@ 6447A5F12A8FDD1B00739CCF /* BitUtil.swift in Sources */, 64963B6A2A8E545C001E40F7 /* IEventType.swift in Sources */, 6428EF282AB0856400F54F59 /* DXInstrumentProfileConnectionState+Ext.swift in Sources */, + 64BDDB222AD6CC9A00694210 /* SpreadOrder.swift in Sources */, 640C3FCE2A61788500555161 /* CandleExchange.swift in Sources */, 641BCBBC2A20ED8100FE23C2 /* DXEndpointObserver.swift in Sources */, 6486B9792AD04F4000D8D5FA /* OrderBase.swift in Sources */, @@ -2165,6 +2178,7 @@ 64C771FF2A9504ED009868C2 /* SnapshotProcessor.swift in Sources */, 64C771F82A94B88C009868C2 /* TimeAndSaleType.swift in Sources */, 641BDD5B2AC72BD400236B78 /* ConcurrentWeakHashTable.swift in Sources */, + 64BDDB242AD6F10200694210 /* Scope.swift in Sources */, 6447A5DF2A8E56FC00739CCF /* IIndexedEvent.swift in Sources */, 64104FC52A26059B00D1FC41 /* ConcurrentSet.swift in Sources */, 64BA925F2A306B9600BE26A0 /* Profile.swift in Sources */, @@ -2178,6 +2192,7 @@ 642BE4CA2A2E1C640052340A /* MarketEvent.swift in Sources */, 6486B95B2AD015B400D8D5FA /* PriceType.swift in Sources */, 8088D76629C0FBCE00F240CB /* IsolateThread.swift in Sources */, + 64BDDB202AD6CC8300694210 /* AnalyticOrder.swift in Sources */, 64AAF0532A8113E800E8942B /* String+Range.swift in Sources */, 642BE4D02A2F1D3C0052340A /* Quote+Ext.swift in Sources */, 641AC1A92A61AE4000EF6D6C /* DXAliases.swift in Sources */, @@ -2187,6 +2202,7 @@ 6486B9672AD0390800D8D5FA /* GreeksMapper.swift in Sources */, 64AAF0572A82A3FC00E8942B /* ICandleSymbolProperty.swift in Sources */, 64437A922A9DF1DE005929B2 /* NativeInstrumentProfileReader.swift in Sources */, + 64BDDB1E2AD6CC6A00694210 /* Order.swift in Sources */, 64437A8F2A9DEE6F005929B2 /* InstrumentProfile.swift in Sources */, 64C771FA2A94D692009868C2 /* ShortSaleRestriction.swift in Sources */, 64656F5E2A1B97F2006A0B19 /* NativeFeed.swift in Sources */, diff --git a/DXFeedFramework/Events/Market/Extra/IndexedEventSource.swift b/DXFeedFramework/Events/Market/Extra/IndexedEventSource.swift index 05597fd75..9fbc4553e 100644 --- a/DXFeedFramework/Events/Market/Extra/IndexedEventSource.swift +++ b/DXFeedFramework/Events/Market/Extra/IndexedEventSource.swift @@ -16,14 +16,14 @@ public class IndexedEventSource { public let name: String /// The default source with zero identifier for all events that do not support multiple sources. - public static let defaultSource = IndexedEventSource(identifier: 0, name: "DEFAULT") + public static let defaultSource = IndexedEventSource( 0, "DEFAULT") /// Initializes a new instance of the ``IndexedEventSource`` class. /// /// - Parameters: /// - identifier: The identifier /// - name: The name of identifier - public init(identifier: Int, name: String) { + public init(_ identifier: Int, _ name: String) { self.name = name self.identifier = identifier } diff --git a/DXFeedFramework/Events/Market/Extra/OrderAction.swift b/DXFeedFramework/Events/Market/Extra/OrderAction.swift index 90770bb76..5f19f7783 100644 --- a/DXFeedFramework/Events/Market/Extra/OrderAction.swift +++ b/DXFeedFramework/Events/Market/Extra/OrderAction.swift @@ -7,10 +7,10 @@ import Foundation -public enum OrderAction { +public enum OrderAction: Int, CaseIterable { /// Default enum value for orders that do not support "Full Order Book" and for backward compatibility - /// action must be derived from other ``Order`` - case undefined + case undefined = 0 /// New Order is added to Order Book. case new /// Order is modified and price-time-priority is not maintained (i.e. order has re-entered Order Book). @@ -28,3 +28,11 @@ public enum OrderAction { /// Prior Trade/Order Execution bust case bust } + +internal class OrderActionExt { + private static let values: [OrderAction] = EnumUtil.createEnumBitMaskArrayByValue(defaultValue: .undefined, allCases: OrderAction.allCases) + + public static func valueOf(value: Int) -> OrderAction { + return values[value] + } +} diff --git a/DXFeedFramework/Events/Market/Extra/OrderBase.swift b/DXFeedFramework/Events/Market/Extra/OrderBase.swift index 60f8b040a..229836e04 100644 --- a/DXFeedFramework/Events/Market/Extra/OrderBase.swift +++ b/DXFeedFramework/Events/Market/Extra/OrderBase.swift @@ -7,34 +7,48 @@ import Foundation -class OrderBase: MarketEvent, IIndexedEvent, CustomStringConvertible { - var eventSource: IndexedEventSource = .defaultSource +public class OrderBase: MarketEvent, IIndexedEvent, CustomStringConvertible { - var type: EventCode = .orderBase + public var type: EventCode = .orderBase -// var eventSource: IndexedEventSource { -// get { -// -// -// } -// set { -// -// } -// } + public var eventSource: IndexedEventSource { + get { + var sourceId = index >> 48 + if !OrderSource.isSpecialSourceId(sourceId: Int(sourceId)) { + sourceId = index >> 32 + } + if let value = try? OrderSource.valueOf(identifier: Int(sourceId)) { + return value + } + fatalError("Incorrect value for eventsource \(self)") + } + set { + let shift = OrderSource.isSpecialSourceId(sourceId: newValue.identifier) ? 48 : 32 + let mask = OrderSource.isSpecialSourceId(sourceId: Int(index >> 48)) ? ~(Long(-1) << 48) : ~(Long(-1) << 32) + index = (Long(newValue.identifier << shift)) | (index & mask) + } + } - var eventFlags: Int32 = 0 + public var eventFlags: Int32 = 0 - var index: Long = 0 + public var index: Long = 0 { + didSet { + if index < 0 { + index = 0 + print("Negative index for \(self)") + } + } + } - var eventSymbol: String + public var eventSymbol: String - var eventTime: Int64 = 0 + public var eventTime: Int64 = 0 /* * Flags property has several significant bits that are packed into an integer in the following way: * 31..15 14..11 10..4 3 2 1 0 - * +--------+--------+--------+----+----+----+----+ + *), \--------+--------+--------+----+----+----+----+ * | | Action |Exchange| Side | Scope | - * +--------+--------+--------+----+----+----+----+ + *), \--------+--------+--------+----+----+----+----+ */ // ACTION values are taken from OrderAction enum. @@ -57,13 +71,13 @@ class OrderBase: MarketEvent, IIndexedEvent, CustomStringConvertible { * Index field has 2 formats depending on whether source is "special" (see OrderSource.IsSpecialSourceId()). * Note: both formats are IMPLEMENTATION DETAILS, they are subject to change without notice. * 63..48 47..32 31..16 15..0 - * +--------+--------+--------+--------+ + *), \--------+--------+--------+--------+ * | Source |Exchange| Index | <- "special" order sources (non-printable id with exchange) - * +--------+--------+--------+--------+ + *), \--------+--------+--------+--------+ * 63..48 47..32 31..16 15..0 - * +--------+--------+--------+--------+ + *), \--------+--------+--------+--------+ * | Source | Index | <- generic order sources (alphanumeric id without exchange) - * +--------+--------+--------+--------+ + *), \--------+--------+--------+--------+ * Note: when modifying formats track usages of getIndex/setIndex, getSource/setSource and isSpecialSourceId * methods in order to find and modify all code dependent on current formats. */ @@ -71,9 +85,9 @@ class OrderBase: MarketEvent, IIndexedEvent, CustomStringConvertible { /* * EventFlags property has several significant bits that are packed into an integer in the following way: * 31..7 6 5 4 3 2 1 0 - * +---------+----+----+----+----+----+----+----+ + *), \---------+----+----+----+----+----+----+----+ * | | SM | | SS | SE | SB | RE | TX | - * +---------+----+----+----+----+----+----+----+ + *), \---------+----+----+----+----+----+----+----+ */ /// Gets or sets time and sequence of this order packaged into single long value @@ -152,6 +166,11 @@ extension OrderBase { public func getExchangeCode() -> Character { return Character(BitUtil.getBits(flags: Int(flags), mask: OrderBase.exchangeMask, shift: OrderBase.exchangeShift)) } + + /// Gets exchange code of this order. + public func getExchangeCode() -> Int { + return BitUtil.getBits(flags: Int(flags), mask: OrderBase.exchangeMask, shift: OrderBase.exchangeShift) + } /// Sets exchange code of this order. /// /// - Throws: ``ArgumentException/exception(_:)`` @@ -178,4 +197,113 @@ extension OrderBase { } } + /// Gets or sets time of this order. + /// Time is measured in milliseconds between the current time and midnight, January 1, 1970 UTC. + public var time: Long { + get { + ((timeSequence >> 32) * 1000) + ((timeSequence >> 22) & 0x3ff) + } + set { + timeSequence = Long(TimeUtil.getSecondsFromTime(newValue) << 32) | + (Long(TimeUtil.getMillisFromTime(newValue)) << 22) | + Long(getSequence()) + } + } + + /// Gets sequence number of this order to distinguish events that have the same ``time``. + /// This sequence number does not have to be unique and + /// does not need to be sequential. Sequence can range from 0 to ``MarketEventConst/maxSequence``. + public func getSequence() -> Int { + return Int(index) & Int(MarketEventConst.maxSequence) + } + /// Sets sequence number of this order to distinguish quotes that have the same ``time``. + /// This sequence number does not have to be unique and + /// does not need to be sequential. Sequence can range from 0 to ``MarketEventConst/maxSequence``. + /// - Throws: ``ArgumentException/exception(_:)`` + public func setSequence(_ sequence: Int) throws { + if sequence < 0 || sequence > MarketEventConst.maxSequence { + throw ArgumentException.exception( + "Sequence(\(sequence) is < 0 or > MaxSequence(\(MarketEventConst.maxSequence)" + ) + } + index = Long(index & ~Long(MarketEventConst.maxSequence)) | Long(sequence) + } + + /// Gets or sets timestamp of the original event in nanoseconds + /// Time is measured in nanoseconds between the current time and midnight, January 1, 1970 UTC. + public var timeNanos: Long { + get { + TimeNanosUtil.getNanosFromMillisAndNanoPart(time, timeNanoPart) + } + set { + time = TimeNanosUtil.getNanoPartFromNanos(newValue) + timeNanoPart = Int32(TimeNanosUtil.getNanoPartFromNanos(newValue)) + } + } + + /// Gets or sets action of this order. + /// Returns order action if available, otherwise ``OrderAction/undefined`` + public var action: OrderAction { + get { + return OrderActionExt.valueOf(value: BitUtil.getBits(flags: Int(flags), + mask: OrderBase.actionMask, + shift: OrderBase.actionShift)) + } + set { + flags = Int32(BitUtil.setBits(flags: Int(flags), + mask: OrderBase.actionMask, + shift: OrderBase.actionShift, + bits: newValue.rawValue)) + } + } + + /// Gets or sets scope of this order. + public var scope: Scope { + get { + return ScopeExt.valueOf(value: BitUtil.getBits(flags: Int(flags), + mask: OrderBase.scopeMask, + shift: OrderBase.scopeShift)) + } + + set { + flags = Int32(BitUtil.setBits(flags: Int(flags), + mask: OrderBase.scopeMask, + shift: OrderBase.scopeShift, + bits: newValue.rawValue)) + } + } + + /// Returns string representation of this candle event. + func toString() -> String { + return "OrderBase{\(baseFieldsToString())}" + } + + /// Returns string representation of this candle fields. + func baseFieldsToString() -> String { + return +""" +\(eventSymbol), \ +eventTime=\(TimeUtil.toLocalDateString(millis: eventTime)), \ +source=\(eventSource), \ +eventFlags=0x\(String(format: "%02X", eventFlags)), \ +index=0x\(String(format: "%02X", index)), \ +time=\(TimeUtil.toLocalDateString(millis: time)), \ +sequence=\(getSequence()), \ +timeNanoPart=\(timeNanoPart), \ +action=\(action), \ +actionTime=\(TimeUtil.toLocalDateString(millis: actionTime)), \ +orderId=\(orderId), \ +auxOrderId=\(auxOrderId), \ +price=\(price), \ +size=\(size), \ +executedSize=\(executedSize), \ +count=\(count), \ +exchange=\(StringUtil.encodeChar(char: Int16(getExchangeCode()))), \ +side=\(orderSide), \ +scope=\(scope), \ +tradeId=\(tradeId), \ +tradePrice=\(tradePrice), \ +tradeSize=\(tradeSize) +""" + } } diff --git a/DXFeedFramework/Events/Market/Extra/OrderSource.swift b/DXFeedFramework/Events/Market/Extra/OrderSource.swift index 0d2cf923d..2b61871e5 100644 --- a/DXFeedFramework/Events/Market/Extra/OrderSource.swift +++ b/DXFeedFramework/Events/Market/Extra/OrderSource.swift @@ -37,16 +37,143 @@ public class OrderSource: IndexedEventSource { static private let sourcesByName = ConcurrentDict() private let sourcesByIdCache = NSCache() - override init(identifier: Int, name: String) { + /// The default source with zero identifier for all events that do not support multiple sources. + public static let defaultOrderSource = try? OrderSource(0, "DEFAULT", pubOrder | pubAnalyticOrder | pubSpreadOrder | fullOrderBook) + + /// Bid side of a composite ``Quote`` + /// + /// It is a synthetic source. + /// The subscription on composite ``Quote`` event is observed when this source is subscribed to. + public static let compsoiteBid = try? OrderSource(1, "DEFAULT", pubOrder | pubAnalyticOrder | pubSpreadOrder | fullOrderBook) + /// Ask side of a composite ``Quote``. + /// It is a synthetic source. + /// The subscription on composite ``Quote`` event is observed when this source is subscribed to. + public static let compsoiteAsk = try? OrderSource(2, "COMPOSITE_ASK", 0) + /// Bid side of a regional ``Quote``. + /// It is a synthetic source. + /// The subscription on regional ``Quote`` event is observed when this source is subscribed to. + + public static let regionalBid = try? OrderSource(3, "REGIONAL_BID", 0) + /// Ask side of a regional ``Quote``. + /// It is a synthetic source. + /// The subscription on regional ``Quote`` event is observed when this source is subscribed to. + + public static let regionalAsk = try? OrderSource(4, "REGIONAL_ASK", 0) + /// Bid side of an aggregate order book (futures depth and NASDAQ Level II). + /// This source cannot be directly published via dxFeed API, but otherwise it is fully operational. + public static let agregateBid = try? OrderSource(5, "AGGREGATE_BID", 0) + + /// Ask side of an aggregate order book (futures depth and NASDAQ Level II). + /// This source cannot be directly published via dxFeed API, but otherwise it is fully operational. + public static let agregateAsk = try? OrderSource(6, "AGGREGATE_ASK", 0) + + /// NASDAQ Total View. + public static let NTV = try? OrderSource("NTV", pubOrder | fullOrderBook) + + /// NASDAQ Total View. Record for price level book. + public static let ntv = try? OrderSource("ntv", pubOrder) + + /// NASDAQ Futures Exchange. + public static let NFX = try? OrderSource("NFX", pubOrder) + + /// NASDAQ eSpeed. + public static let ESPD = try? OrderSource("ESPD", pubOrder) + + /// NASDAQ Fixed Income. + public static let XNFI = try? OrderSource("XNFI", pubOrder) + + /// Intercontinental Exchange. + public static let ICE = try? OrderSource("ICE", pubOrder) + + /// International Securities Exchange. + public static let ISE = try? OrderSource("ISE", pubOrder | pubSpreadOrder) + + /// Direct-Edge EDGA Exchange. + public static let DEA = try? OrderSource("DEA", pubOrder) + + /// Direct-Edge EDGX Exchange. + public static let DEX = try? OrderSource("DEX", pubOrder) + + /// Bats BYX Exchange. + public static let BYX = try? OrderSource("BYX", pubOrder) + + /// Bats BZX Exchange. + public static let BZX = try? OrderSource("BZX", pubOrder) + + /// Bats Europe BXE Exchange. + public static let BATE = try? OrderSource("BATE", pubOrder) + + /// Bats Europe CXE Exchange. + public static let CHIX = try? OrderSource("CHIX", pubOrder) + + /// Bats Europe DXE Exchange. + public static let CEUX = try? OrderSource("CEUX", pubOrder) + + /// Bats Europe TRF. + public static let BXTR = try? OrderSource("BXTR", pubOrder) + + /// Borsa Istanbul Exchange. + public static let IST = try? OrderSource("IST", pubOrder) + + /// Borsa Istanbul Exchange. Record for particular top 20 order book. + public static let BI20 = try? OrderSource("BI20", pubOrder) + + /// ABE (abe.io) exchange. + public static let ABE = try? OrderSource("ABE", pubOrder) + + /// FAIR (FairX) exchange. + public static let FAIR = try? OrderSource("FAIR", pubOrder) + + /// CME Globex. + public static let GLBX = try? OrderSource("GLBX", pubOrder | pubAnalyticOrder) + + /// CME Globex. Record for price level book. + public static let glbx = try? OrderSource("glbx", pubOrder) + + /// Eris Exchange group of companies. + public static let ERIS = try? OrderSource("ERIS", pubOrder) + + /// Eurex Exchange. + public static let XEUR = try? OrderSource("XEUR", pubOrder) + + /// Eurex Exchange. Record for price level book. + public static let xeur = try? OrderSource("xeur", pubOrder) + + /// CBOE Futures Exchange. + public static let CFE = try? OrderSource("CFE", pubOrder) + + /// CBOE Options C2 Exchange. + public static let C2OX = try? OrderSource("C2OX", pubOrder) + + /// Small Exchange. + public static let SMFE = try? OrderSource("SMFE", pubOrder) + + /// Small Exchange. Record for price level book. + public static let smfe = try? OrderSource("smfe", pubOrder) + + /// Investors exchange. Record for price level book. + public static let iex = try? OrderSource("iex", pubOrder) + + /// Members Exchange. + public static let MEMX = try? OrderSource("MEMX", pubOrder) + + /// Members Exchange. Record for price level book. + public static let memx = try? OrderSource("memx", pubOrder) + + override init(_ identifier: Int, _ name: String) { self.pubFlags = 0 self.isBuiltin = false - super.init(identifier: identifier, name: name) + super.init(identifier, name) } - init(identifier: Int, name: String, pubFlags: Int) throws { + convenience init(_ name: String, _ pubFlags: Int) throws { + try self.init(OrderSource.composeId(name: name), name, pubFlags) + } + + init(_ identifier: Int, _ name: String, _ pubFlags: Int) throws { self.pubFlags = pubFlags self.isBuiltin = true - super.init(identifier: identifier, name: name) + super.init(identifier, name) switch identifier { case _ where identifier < 0: throw ArgumentException.exception("id is negative") @@ -61,7 +188,8 @@ public class OrderSource: IndexedEventSource { print("") } // Flag FullOrderBook requires that source must be publishable. - if (pubFlags & OrderSource.fullOrderBook) != 0 && (pubFlags & (OrderSource.pubOrder | OrderSource.pubAnalyticOrder | OrderSource.pubSpreadOrder)) == 0 { + if (pubFlags & OrderSource.fullOrderBook) != 0 && + (pubFlags & (OrderSource.pubOrder | OrderSource.pubAnalyticOrder | OrderSource.pubSpreadOrder)) == 0 { throw ArgumentException.exception("Unpublishable full order book order") } @@ -110,7 +238,7 @@ public class OrderSource: IndexedEventSource { if identifier >> index == 0 { // Skip highest contiguous zeros. continue } - var char = Character(((identifier >> index) & 0xff)) + let char = Character(((identifier >> index) & 0xff)) try check(char: String(char)) name.append(char) } @@ -121,25 +249,59 @@ public class OrderSource: IndexedEventSource { return (pubFlags & OrderSource.fullOrderBook) != 0 } + /// Gets a value indicating whether the given event type can be directly published with this source. + /// + /// Subscription on such sources can be observed directly via ``DXPublisher`` + /// Subscription on such sources is observed via instances of ``IndexedEventSubscriptionSymbol`` + /// - Parameters: + /// - eventype : Possible values ``Order``, ``AnalyticOrder``, ``SpreadOrder`` + /// - Returns: true- events can be directly published with this source + /// - Throws: ``ArgumentException/exception(_:)`` + public func isPublishable(eventType: AnyClass.Type) throws -> Bool { + return pubFlags & (try OrderSource.getEventTypeMask(eventType)) != 0 + } + + /// Returns order source for the specified source identifier. - /// - Throws: ``ArgumentException/exception(_:)``. Rethrows exception from Java. + /// - Throws: ``ArgumentException/exception(_:)`` public static func valueOf(identifier: Int) throws -> OrderSource { return try OrderSource.sourcesById.tryGetValue(key: identifier) { let name = try decodeName(identifier: identifier) - let source = OrderSource(identifier: identifier, name: name) + let source = OrderSource(identifier, name) return source } } /// Returns order source for the specified source name. /// /// The name must be either predefined, or contain at most 4 alphanumeric characters. - /// - Throws: ``ArgumentException/exception(_:)``. Rethrows exception from Java. + /// - Throws: ``ArgumentException/exception(_:)`` public static func valueOf(name: String) throws -> OrderSource { return try OrderSource.sourcesByName.tryGetValue(key: name) { let identifier = try composeId(name: name) - let source = OrderSource(identifier: identifier, name: name) + let source = OrderSource(identifier, name) return source } } + /// Gets type mask by specified event type. + /// + /// - Parameters: + /// - eventype : Possible values ``Order``, ``AnalyticOrder``, ``SpreadOrder`` + /// - Returns: The mask for event class. + /// - Throws: ``ArgumentException/exception(_:)`` + public static func getEventTypeMask(_ eventType: AnyClass) throws -> Int { + if eventType == Order.self { + return pubOrder + } + + if eventType == AnalyticOrder.self { + return pubAnalyticOrder + } + + if eventType == SpreadOrder.self { + return pubSpreadOrder + } + throw ArgumentException.exception("Invalid order event type: \(eventType)") + } + } diff --git a/DXFeedFramework/Events/Market/Extra/Scope.swift b/DXFeedFramework/Events/Market/Extra/Scope.swift new file mode 100644 index 000000000..de455bcd9 --- /dev/null +++ b/DXFeedFramework/Events/Market/Extra/Scope.swift @@ -0,0 +1,32 @@ +// +// Scope.swift +// DXFeedFramework +// +// Created by Aleksey Kosylo on 11.10.23. +// + +import Foundation + +/// Scope of an order. +public enum Scope: Int, CaseIterable { + /// Represents best bid or best offer for the whole market. + case composite = 0 + + /// Represents best bid or best offer for a given exchange code. + case regional + + /// Represents aggregate information for a given price level or + /// best bid or best offer for a given market maker. + case aggregate + + /// Represents individual order on the market. + case order +} + +public class ScopeExt { + private static let values: [Scope] = EnumUtil.createEnumBitMaskArrayByValue(defaultValue: .composite, allCases: Scope.allCases) + + public static func valueOf(value: Int) -> Scope { + return values[value] + } +} diff --git a/DXFeedFramework/Native/Events/Markets/AnalyticOrder.swift b/DXFeedFramework/Native/Events/Markets/AnalyticOrder.swift new file mode 100644 index 000000000..d8c14c7fe --- /dev/null +++ b/DXFeedFramework/Native/Events/Markets/AnalyticOrder.swift @@ -0,0 +1,12 @@ +// +// AnalyticOrder.swift +// DXFeedFramework +// +// Created by Aleksey Kosylo on 11.10.23. +// + +import Foundation + +public class AnalyticOrder: OrderBase { + +} diff --git a/DXFeedFramework/Native/Events/Markets/Order.swift b/DXFeedFramework/Native/Events/Markets/Order.swift new file mode 100644 index 000000000..8e5b9eed5 --- /dev/null +++ b/DXFeedFramework/Native/Events/Markets/Order.swift @@ -0,0 +1,12 @@ +// +// Order.swift +// DXFeedFramework +// +// Created by Aleksey Kosylo on 11.10.23. +// + +import Foundation + +public class Order: OrderBase { + +} diff --git a/DXFeedFramework/Native/Events/Markets/SpreadOrder.swift b/DXFeedFramework/Native/Events/Markets/SpreadOrder.swift new file mode 100644 index 000000000..70d371303 --- /dev/null +++ b/DXFeedFramework/Native/Events/Markets/SpreadOrder.swift @@ -0,0 +1,12 @@ +// +// SpreadOrder.swift +// DXFeedFramework +// +// Created by Aleksey Kosylo on 11.10.23. +// + +import Foundation + +public class SpreadOrder: OrderBase { + +} diff --git a/DXFeedFrameworkTests/OrderSourceTest.swift b/DXFeedFrameworkTests/OrderSourceTest.swift index 26981cb67..1b6bc4646 100644 --- a/DXFeedFrameworkTests/OrderSourceTest.swift +++ b/DXFeedFrameworkTests/OrderSourceTest.swift @@ -48,8 +48,8 @@ final class OrderSourceTest: XCTestCase { func testCreateOrderSourceWithDuplicateName() throws { do { - _ = try OrderSource(identifier: 44, name: "COMPOSITE_ASK", pubFlags: 0) - _ = try OrderSource(identifier: 33, name: "COMPOSITE_ASK", pubFlags: 0) + _ = try OrderSource( 44, "COMPOSITE_ASK", 0) + _ = try OrderSource( 33, "COMPOSITE_ASK", 0) } catch ArgumentException.exception(let message) { XCTAssert(message.contains("name"), "Wrong message \(message)") return @@ -61,8 +61,8 @@ final class OrderSourceTest: XCTestCase { func testCreateOrderSourceWithDuplicateId() throws { do { - _ = try OrderSource(identifier: 3, name: "COMPOSITE_ASK1", pubFlags: 0) - _ = try OrderSource(identifier: 3, name: "COMPOSITE_ASK3", pubFlags: 0) + _ = try OrderSource(3, "COMPOSITE_ASK1", 0) + _ = try OrderSource(3, "COMPOSITE_ASK3", 0) } catch ArgumentException.exception(let message) { XCTAssert(message.contains("id"), "Wrong message \(message)") return @@ -74,8 +74,8 @@ final class OrderSourceTest: XCTestCase { func testCreateOrderSource() throws { do { - _ = try OrderSource(identifier: 5, name: "COMPOSITE_ASK2", pubFlags: 0) - _ = try OrderSource(identifier: 6, name: "COMPOSITE_ASK4", pubFlags: 0) + _ = try OrderSource(5, "COMPOSITE_ASK2", 0) + _ = try OrderSource(6, "COMPOSITE_ASK4", 0) } catch { XCTAssert(false, "undefined error \(error)") }