Skip to content

Commit

Permalink
add OptionSale
Browse files Browse the repository at this point in the history
  • Loading branch information
kosyloa committed Oct 13, 2023
1 parent 7af6a20 commit 7428d78
Show file tree
Hide file tree
Showing 14 changed files with 428 additions and 35 deletions.
12 changes: 12 additions & 0 deletions DXFeedFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@
64AAF0552A82499A00E8942B /* ConcurrentDict.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AAF0542A82499A00E8942B /* ConcurrentDict.swift */; };
64AAF0572A82A3FC00E8942B /* ICandleSymbolProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AAF0562A82A3FC00E8942B /* ICandleSymbolProperty.swift */; };
64AAF0592A83EA0000E8942B /* Double+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AAF0582A83EA0000E8942B /* Double+Ext.swift */; };
64ABC0FF2AD91E6500904D78 /* OptionSale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64ABC0FE2AD91E6500904D78 /* OptionSale.swift */; };
64ABC1012AD9284900904D78 /* OptionSale+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64ABC1002AD9284900904D78 /* OptionSale+Ext.swift */; };
64ABC1032AD9294D00904D78 /* OptionSaleMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64ABC1022AD9294C00904D78 /* OptionSaleMapper.swift */; };
64ACBCCF2A27851C00032C53 /* FeedTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64104FC02A210DDD00D1FC41 /* FeedTest.swift */; };
64ACBCD52A2789EF00032C53 /* TestListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BCBB92A1FB42500FE23C2 /* TestListener.swift */; };
64ACBCD72A27981900032C53 /* ListNative+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64ACBCD62A27981900032C53 /* ListNative+Ext.swift */; };
Expand Down Expand Up @@ -679,6 +682,9 @@
64AAF0542A82499A00E8942B /* ConcurrentDict.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentDict.swift; sourceTree = "<group>"; };
64AAF0562A82A3FC00E8942B /* ICandleSymbolProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICandleSymbolProperty.swift; sourceTree = "<group>"; };
64AAF0582A83EA0000E8942B /* Double+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Ext.swift"; sourceTree = "<group>"; };
64ABC0FE2AD91E6500904D78 /* OptionSale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionSale.swift; sourceTree = "<group>"; };
64ABC1002AD9284900904D78 /* OptionSale+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OptionSale+Ext.swift"; sourceTree = "<group>"; };
64ABC1022AD9294C00904D78 /* OptionSaleMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionSaleMapper.swift; sourceTree = "<group>"; };
64ACBCD62A27981900032C53 /* ListNative+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListNative+Ext.swift"; sourceTree = "<group>"; };
64ACBCD82A279F7900032C53 /* DXEventListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DXEventListener.swift; sourceTree = "<group>"; };
64ACBCDC2A28978600032C53 /* Symbol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Symbol.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -940,6 +946,7 @@
64BDDB1F2AD6CC8300694210 /* AnalyticOrder.swift */,
64BDDB212AD6CC9A00694210 /* SpreadOrder.swift */,
649706832AD82B070068FF88 /* Series.swift */,
64ABC0FE2AD91E6500904D78 /* OptionSale.swift */,
);
path = Market;
sourceTree = "<group>";
Expand Down Expand Up @@ -1014,6 +1021,8 @@
64BDDB2F2AD7E5A500694210 /* AnalyticOrderMapper.swift */,
649706852AD832860068FF88 /* Series+Ext.swift */,
64E3637A2AD83459002E2B0D /* SeriesMapper.swift */,
64ABC1002AD9284900904D78 /* OptionSale+Ext.swift */,
64ABC1022AD9294C00904D78 /* OptionSaleMapper.swift */,
);
path = Markets;
sourceTree = "<group>";
Expand Down Expand Up @@ -2164,11 +2173,14 @@
64C772012A975102009868C2 /* Reference+Util.swift in Sources */,
64656F6D2A1CFB10006A0B19 /* DXEndpointState.swift in Sources */,
6464074D2A9E352E006FF769 /* InstrumentProfileField.swift in Sources */,
64ABC0FF2AD91E6500904D78 /* OptionSale.swift in Sources */,
64ABC1012AD9284900904D78 /* OptionSale+Ext.swift in Sources */,
6486B9632AD0333B00D8D5FA /* Greeks.swift in Sources */,
64E342522AAB29CF00457994 /* InstrumentProfileType.swift in Sources */,
648E98AA2AAF625800BFD219 /* IIndexedEvent+Ext.swift in Sources */,
6447A5E32A8F611700739CCF /* IObservableSubscription.swift in Sources */,
64104FC92A26298D00D1FC41 /* DXFeedSubcription.swift in Sources */,
64ABC1032AD9294D00904D78 /* OptionSaleMapper.swift in Sources */,
64104FD32A277B2D00D1FC41 /* ListNative.swift in Sources */,
649706862AD832860068FF88 /* Series+Ext.swift in Sources */,
642BE4C82A2E1BD40052340A /* QuoteMapper.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions DXFeedFramework/Events/EventCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public enum EventCode: CaseIterable {
case analyticOrder
/// See ``SpreadOrder``
case spreadOrder
/// See ``Series``
case series
/// See ``OptionSale``
case optionSale
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ extension MarketEvent {
public var series: Series {
return (self as? Series)!
}
/// Use only for event.type is ``EventCode/optionSale``
public var optionSale: OptionSale {
return (self as? OptionSale)!
}
}
8 changes: 6 additions & 2 deletions DXFeedFramework/Events/Market/Extra/OrderBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,16 @@ extension OrderBase {
}
/// Gets exchange code of this order.
public func getExchangeCode() -> Character {
return Character(BitUtil.getBits(flags: Int(flags), mask: OrderBase.exchangeMask, shift: OrderBase.exchangeShift))
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)
return BitUtil.getBits(flags: Int(flags),
mask: OrderBase.exchangeMask,
shift: OrderBase.exchangeShift)
}
/// Sets exchange code of this order.
///
Expand Down
6 changes: 4 additions & 2 deletions DXFeedFramework/Events/Market/Extra/OrderSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ public class OrderSource: IndexedEventSource {
private let sourcesByIdCache = NSCache<AnyObject, OrderSource>()

/// 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)
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, "COMPOSITE_BID", pubOrder | pubAnalyticOrder | pubSpreadOrder | fullOrderBook)
public static let compsoiteBid =
try? OrderSource(1, "COMPOSITE_BID", 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.
Expand Down
265 changes: 265 additions & 0 deletions DXFeedFramework/Events/Market/OptionSale.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
//
// OptionSale.swift
// DXFeedFramework
//
// Created by Aleksey Kosylo on 13.10.23.
//

import Foundation

/// Option Sale event represents a trade or another market event with the price
/// (for example, market open/close price, etc.) for each option symbol listed under the specified Underlying.
///
/// Option Sales are intended to provide information about option trades **in a continuous time slice** with
/// the additional metrics, like Option Volatility, Option Delta, and Underlying Price.
///
/// Option Sale events have unique ``index``
/// which can be used for later correction/cancellation processing.
///
/// [For more details see](https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/market/OptionSale.html)
public class OptionSale: MarketEvent, IIndexedEvent {
public var type: EventCode = .optionSale

public var eventSource: IndexedEventSource = .defaultSource

public var eventFlags: Int32 = 0

public var index: Long = 0

public var eventSymbol: String

public var eventTime: Int64 = 0

/*
* 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 series packaged into single long value.
///
/// This method is intended for efficient series time priority comparison.
/// **Do not use this method directly**
/// Change ``time`` and/or ``setSequence(_:)``
public var timeSequence: Int64 = 0
/// Gets or sets microseconds and nanoseconds time part of event.
public var timeNanoPart: Int32 = 0
/// Gets or sets exchange code of this option sale event.
public var exchangeCode: Int16 = 0
/// Gets or sets price of this option sale event.
public var price: Double = .nan
/// Gets or sets size this option sale event as floating number with fractions.
public var size: Double = .nan
/// Gets or sets the current bid price on the market when this option sale event had occurred.
public var bidPrice: Double = .nan
/// Gets or sets the current ask price on the market when this option sale event had occurred.
public var askPrice: Double = .nan
/// Gets or sets sale conditions provided for this event by data feed.
///
/// This field format is specific for every particular data feed.
public var exchangeSaleConditions: String = ""
/// Gets or sets implementation-specific flags.
///
/// **Do not use this method directly.**
public var flags: Int32 = 0
/// Gets or sets underlying price at the time of this option sale event.
public var underlyingPrice: Double = .nan
/// Gets or sets Black-Scholes implied volatility of the option at the time of this option sale event.
public var volatility: Double = .nan
/// Gets or sets option delta at the time of this option sale event.
/// Delta is the first derivative of an option price by an underlying price.
public var delta: Double = .nan
/// Gets or sets option symbol of this event.
public var optionSymbol: String = ""

public init(_ eventSymbol: String) {
self.eventSymbol = eventSymbol
}
}

extension OptionSale {
/// Gets or sets timestamp of the event in milliseconds.
/// 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) |
Int64(getSequence())
}
}

/// Gets or sets time of the last trade 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 sequence number of the last trade to distinguish trades 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(timeSequence) & Int(MarketEventConst.maxSequence)
}

/// Sets sequence number of the last trade to distinguish trades 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)"
)
}
timeSequence = (timeSequence & Long(~MarketEventConst.maxSequence)) | Int64(sequence)
}

/// Gets TradeThroughExempt flag of this option sale event.
public func getTradeThroughExempt() -> Character {
Character(BitUtil.getBits(flags: Int(flags),
mask: TimeAndSale.tteMask,
shift: TimeAndSale.tteShift))
}
/// Sets TradeThroughExempt flag of this option sale event.
///
/// - Throws: ``ArgumentException/exception(_:)``
public func setTradeThroughExempt(_ char: Character) throws {
try StringUtil.checkChar(char: char, mask: TimeAndSale.tteMask, name: "tradeThroughExempt")
if let value = char.unicodeScalars.first?.value {
flags = Int32(BitUtil.setBits(flags: Int(flags),
mask: TimeAndSale.tteMask,
shift: TimeAndSale.tteShift,
bits: Int(value)))
}
}

/// Gets or sets aggressor side of this option sale event.
public var aggressorSide: Side {
get {
Side.valueOf(Int(BitUtil.getBits(flags: Int(flags),
mask: TimeAndSale.sideMask,
shift: TimeAndSale.sideShift)))
}
set {
flags = Int32(BitUtil.setBits(flags: Int(flags),
mask: TimeAndSale.sideMask,
shift: TimeAndSale.sideShift,
bits: newValue.rawValue))
}
}

/// Gets or sets a value indicating whether this event represents a spread leg.
public var isSpreadLeg: Bool {
get {
(flags & Int32(TimeAndSale.spreadLeg)) != 0
}
set {
flags = newValue ? (flags | Int32(TimeAndSale.spreadLeg)) : (flags & ~Int32(TimeAndSale.spreadLeg))
}
}

/// Gets or sets a value indicating whether this event represents an extended trading hours sale.
public var isExtendedTradingHours: Bool {
get {
(flags & Int32(TimeAndSale.eth)) != 0
}
set {
flags = newValue ? (flags | Int32(TimeAndSale.eth)) : (flags & ~Int32(TimeAndSale.eth))
}
}

/// Gets or sets a value indicating whether this event represents a valid intraday tick.
///
/// Note, that a correction for a previously distributed valid tick represents a new valid tick itself,
/// but a cancellation of a previous valid tick does not.
public var isValidTick: Bool {
get {
(flags & Int32(TimeAndSale.validTick)) != 0
}
set {
flags = newValue ? (flags | Int32(TimeAndSale.validTick)) : flags & ~Int32(TimeAndSale.validTick)
}
}

/// Gets or sets type of this option sale event.
public var optionSaleType: TimeAndSaleType {
get {
TimeAndSaleType.valueOf(BitUtil.getBits(flags: Int(flags),
mask: TimeAndSale.typeMask,
shift: TimeAndSale.typeShift))
}
set {
flags = Int32(BitUtil.setBits(flags: Int(flags),
mask: TimeAndSale.typeMask,
shift: TimeAndSale.typeShift,
bits: newValue.rawValue))
}
}

/// Gets a value indicating whether this is a new event (not cancellation or correction).
///
/// It is true for newly created option sale event.
public var isNew: Bool {
optionSaleType == .new
}

/// Gets a value indicating whether this is a correction of a previous event.
///
/// It is false for newly created option sale event.
/// true if this is a correction of a previous event.
public var isCorrection: Bool {
optionSaleType == .correction
}

/// Gets a value indicating whether this is a cancellation of a previous event.
///
/// It is false for newly created option sale event.
/// true if this is a cancellation of a previous event.
public var isCancel: Bool {
optionSaleType == .cancel
}

/// Returns string representation of this time and sale event.
public func toString() -> String {
return """
OptionSale{\(eventSymbol), \
eventTime=\(TimeUtil.toLocalDateString(millis: eventTime)), \
eventFlags=0x\(String(format: "%02X", eventFlags)), \
index=0x\(String(format: "%02X", index)), \
time=\(TimeUtil.toLocalDateString(millis: time)), \
timeNanoPart=\(timeNanoPart), \
sequence=\(getSequence()), \
exchange=\(StringUtil.encodeChar(char: exchangeCode)), \
price=\(price), \
size=\(size), \
bid=\(bidPrice), \
ask=\(askPrice), \
ESC='\(exchangeSaleConditions ?? "null")', \
TTE=\(StringUtil.encodeChar(char: Int16(getTradeThroughExempt().unicodeScalars.first?.value ?? 0))), \
side=\(aggressorSide), \
spread=\(isSpreadLeg), \
ETH=\(isExtendedTradingHours), \
validTick=\(isValidTick), \
type=\(optionSaleType), \
underlyingPrice=\(underlyingPrice), \
volatility=\(volatility), \
delta=\(delta), \
optionSymbol='\(optionSymbol)'\
}
"""
}
}
2 changes: 1 addition & 1 deletion DXFeedFramework/Events/Market/Series.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class Series: MarketEvent, IIndexedEvent {
///
/// This method is intended for efficient series time priority comparison.
/// **Do not use this method directly**
/// Change ``time`` and/or ``sequence``
/// Change ``time`` and/or ``setSequence(_:)``
public var timeSequence: Long = 0
/// Gets or sets day id of expiration.
public var expiration: Int32 = 0
Expand Down
Loading

0 comments on commit 7428d78

Please sign in to comment.