diff --git a/README.md b/README.md index cb01019..96f4660 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Intrinio Java SDK for Real-Time Stock Prices -SDK for working with Intrinio's realtime IEX, NASDAQ Basic, CBOE One, delayed SIP prices, and realtime/delayed [OPRA](https://www.opraplan.com/) options feeds. +SDK for working with Intrinio's realtime IEX, NASDAQ Basic, CBOE One, delayed SIP prices, and realtime/delayed [OPRA](https://www.opraplan.com/) and Options Edge options feeds. [Intrinio](https://intrinio.com/) provides real-time and delayed stock prices via a two-way WebSocket connection. To get started, [subscribe to a real-time data feed](https://docs.intrinio.com/tutorial/websocket) and follow the instructions below. @@ -87,6 +87,7 @@ public record Trade(String symbol, SubProvider subProvider, char marketCenter, d * **`NASDAQ_BASIC`** - NASDAQ Basic in the NASDAQ_BASIC provider. * **`IEX`** - From the IEX exchange in the REALTIME provider. * **`CBOE_ONE`** - From the CBOE One exchanges provider. + * **`EQUITIES_EDGE`** - From the Equities Edge provider. * **marketCenter** - Provides the market center * **price** - the price in USD * **size** - the size of the last trade. @@ -113,6 +114,7 @@ public record Quote(QuoteType type, String symbol, SubProvider subProvider, char * **`NASDAQ_BASIC`** - NASDAQ Basic in the NASDAQ_BASIC provider. * **`IEX`** - From the IEX exchange in the REALTIME provider. * **`CBOE_ONE`** - From the CBOE One exchanges provider. + * **`EQUITIES_EDGE`** - From the Equities Edge provider. * **marketCenter** - Provides the market center * **symbol** - Ticker symbol. * **price** - the price in USD @@ -387,7 +389,7 @@ client.leave() ```json { "apiKey": "", - "provider": "IEX", //or DELAYED_SIP or NASDAQ_BASIC or CBOE_ONE or MANUAL + "provider": "IEX", //or DELAYED_SIP or NASDAQ_BASIC or CBOE_ONE or EQUITIES_EDGE or MANUAL "symbols": [ "AAPL", "MSFT", "GOOG" ], //This is a list of individual tickers to subscribe to, or "lobby" to subscribe to all at once (firehose). "tradesOnly": true, //This indicates whether you only want trade events (true) or you want trade, ask, and bid events (false). "numThreads": 4 //The number of threads to use for processing events. @@ -447,7 +449,7 @@ If a subscription has already been created with one of the `join` methods, data ```json { "apiKey": "", - "provider": "OPRA", //OPRA + "provider": "OPRA", //OPRA, OPTIONS_EDGE "symbols": [ "GOOG__210917C01040000", "MSFT", "AAPL__210917C00130000", "SPY" ], //Individual contracts, or option chains to subscribe to all contracts under a symbol. "numThreads": 4, //The number of threads to use for processing events. "delayed": false //If you have realtime access but want to force 15minute delayed, set this to true. diff --git a/pom.xml b/pom.xml index 191acb0..c4d3833 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.intrinio IntrinioRealtimeJavaSDK - 7.2.0 + 8.0.0 jar IntrinioRealtimeJavaSDK diff --git a/src/SampleApp/CompositeSampleApp.java b/src/SampleApp/CompositeSampleApp.java index 28d630a..ab70e71 100644 --- a/src/SampleApp/CompositeSampleApp.java +++ b/src/SampleApp/CompositeSampleApp.java @@ -28,7 +28,7 @@ public static void run(String[] args){ } //store the most recent values in a simple non-transactional cache that gives contextual information with the event. - intrinio.realtime.composite.DataCache currentDataCache = new CurrentDataCache(); + intrinio.realtime.composite.DataCache currentDataCache = DataCacheFactory.create(); //Initialize Options Client and wire it to the cache //intrinio.realtime.options.OnUnusualActivity optionsUnusualActivityHandler = null; @@ -44,7 +44,7 @@ public static void run(String[] args){ intrinio.realtime.equities.Client equitiesClient = new intrinio.realtime.equities.Client(equitiesTradeHandler, equitiesQuoteHandler, equitiesConfig); //Display trade events with context - currentDataCache.setOnEquitiesTradeUpdated((SecurityData securityData, DataCache dataCache) -> { + currentDataCache.setEquitiesTradeUpdatedCallback((SecurityData securityData, DataCache dataCache, intrinio.realtime.equities.Trade trade) -> { intrinio.realtime.equities.Client.Log(securityData.getTickerSymbol() + " had a trade and also has " + securityData.getAllOptionsContractData().size() + " active contracts"); }); diff --git a/src/intrinio/realtime/composite/BlackScholesGreekCalculator.java b/src/intrinio/realtime/composite/BlackScholesGreekCalculator.java index c1d4ee6..726a8ca 100644 --- a/src/intrinio/realtime/composite/BlackScholesGreekCalculator.java +++ b/src/intrinio/realtime/composite/BlackScholesGreekCalculator.java @@ -1,193 +1,141 @@ -//package intrinio.realtime.composite; -// -//public class BlackScholesGreekCalculator implements GreekCalculator -//{ -// private final double LOW_VOL = 0.0; -// private final double HIGH_VOL = 5.0; -// private final double VOL_TOLERANCE = 0.0001; -// private final double MIN_Z_SCORE = -8.0; -// private final double MAX_Z_SCORE = 8.0; -// private final String DividendYieldKey = "DividendYield"; -// -// @Override -// public Greek calculate(String contract, SecurityData calcData, Double riskFreeInterestRate) { -// intrinio.realtime.equities.Trade underlyingTrade = calcData.getEquitiesTrade(); -// OptionsContractData optionsContractData = calcData.getOptionsContractData(contract); -// if (optionsContractData == null){ -// return null; -// } -// intrinio.realtime.options.Trade latestOptionTrade = optionsContractData.getTrade(); -// intrinio.realtime.options.Quote latestOptionQuote = optionsContractData.getQuote(); -// if (underlyingTrade == null || latestOptionTrade == null || latestOptionQuote == null) -// return null; -// if (latestOptionQuote.askPrice() <= 0.0 || latestOptionQuote.bidPrice() <= 0.0) -// return null; -// if (riskFreeInterestRate == null || riskFreeInterestRate <= 0.0) -// return null; -// -// boolean isPut = latestOptionTrade.isPut(); -// double underlyingPrice = underlyingTrade.price(); -// double strike = latestOptionTrade.getStrikePrice(); -// double daysToExpiration = getDaysToExpiration(latestOptionTrade, latestOptionQuote); -// double dividendYield = calcData.getSupplementaryDatum(DividendYieldKey); -// double marketPrice = (latestOptionQuote.askPrice() + latestOptionQuote.bidPrice()) / 2.0; -// double impliedVolatility = calcImpliedVolatility(isPut, underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice); -// double sigma = impliedVolatility; -// double delta = calcDelta(isPut, underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// double gamma = calcGamma(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// double theta = calcTheta(isPut, underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// double vega = calcVega(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// -// return getGreek(calcData, riskFreeInterestRate, contract, daysToExpiration, marketPrice, impliedVolatility, delta, gamma, theta, vega); -// } -// -// private double calcImpliedVolatilityCall(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice) { -// double low = LOW_VOL, high = HIGH_VOL; -// while ((high - low) > VOL_TOLERANCE){ -// if (calcPriceCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, (high + low) / 2.0, dividendYield) > marketPrice) -// high = (high + low) / 2.0; -// else -// low = (high + low) / 2.0; -// } -// -// return (high + low) / 2.0; -// } -// -// private double calcImpliedVolatilityPut(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice) { -// double low = LOW_VOL, high = HIGH_VOL; -// while ((high - low) > VOL_TOLERANCE){ -// if (calcPricePut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, (high + low) / 2.0, dividendYield) > marketPrice) -// high = (high + low) / 2.0; -// else -// low = (high + low) / 2.0; -// } -// -// return (high + low) / 2.0; -// } -// -// private double calcImpliedVolatility(boolean isPut, double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice){ -// if (isPut) -// return calcImpliedVolatilityPut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice); -// return calcImpliedVolatilityCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice); -// } -// -// private double calcDeltaCall(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// return normalSDist( d1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ); -// } -// -// private double calcDeltaPut(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// return calcDeltaCall( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma) - 1; -// } -// -// private double calcDelta(boolean isPut, double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// if (isPut) -// return calcDeltaPut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// else return calcDeltaCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// } -// -// private double calcGamma(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// return phi( d1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ) / ( underlyingPrice * sigma * Math.sqrt(daysToExpiration) ); -// } -// -// private double calcThetaCall(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// double term1 = underlyingPrice * phi( d1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ) * sigma / ( 2 * Math.sqrt(daysToExpiration) ); -// double term2 = riskFreeInterestRate * strike * Math.exp(-1.0 * riskFreeInterestRate * daysToExpiration) * normalSDist( d2( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ); -// return ( - term1 - term2 ) / 365.25; -// } -// -// private double calcThetaPut(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// double term1 = underlyingPrice * phi( d1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ) * sigma / ( 2 * Math.sqrt(daysToExpiration) ); -// double term2 = riskFreeInterestRate * strike * Math.exp(-1.0 * riskFreeInterestRate * daysToExpiration) * normalSDist( - d2( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ); -// return ( - term1 + term2 ) / 365.25; -// } -// -// private double calcTheta(boolean isPut, double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// if (isPut) -// return calcThetaPut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// else return calcThetaCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); -// } -// -// private double calcVega(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma){ -// return 0.01 * underlyingPrice * Math.sqrt(daysToExpiration) * phi(d1(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield)); -// } -// -// private double d1(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield){ -// double numerator = ( Math.log(underylyingPrice / strike) + (riskFreeInterestRate - dividendYield + 0.5 * Math.pow(sigma, 2.0) ) * daysToExpiration); -// double denominator = ( sigma * Math.sqrt(daysToExpiration)); -// return numerator / denominator; -// } -// -// private double d2(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield){ -// return d1( underylyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) - ( sigma * Math.sqrt(daysToExpiration) ); -// } -// -// private double normalSDist(double z){ -// if (z < MIN_Z_SCORE) -// return 0.0; -// if (z > MAX_Z_SCORE) -// return 1.0; -// double i = 3.0, sum = 0.0, term = z; -// while ((sum + term) != sum){ -// sum = sum + term; -// term = term * z * z / i; -// i += 2.0; -// } -// return 0.5 + sum * phi(z); -// } -// -// private double phi(double x){ -// double numerator = Math.exp(-1.0 * x*x / 2.0); -// double denominator = Math.sqrt(2.0 * Math.PI); -// return numerator / denominator; -// } -// -// private double calcPriceCall(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield){ -// double d1 = d1( underylyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ); -// double discounted_underlying = Math.exp(-1.0 * dividendYield * daysToExpiration) * underylyingPrice; -// double probability_weighted_value_of_being_exercised = discounted_underlying * normalSDist( d1 ); -// -// double d2 = d1 - ( sigma * Math.sqrt(daysToExpiration) ); -// double discounted_strike = Math.exp(-1.0 * riskFreeInterestRate * daysToExpiration) * strike; -// double probability_weighted_value_of_discounted_strike = discounted_strike * normalSDist( d2 ); -// -// return probability_weighted_value_of_being_exercised - probability_weighted_value_of_discounted_strike; -// } -// -// private double calcPricePut(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield){ -// double d2 = d2( underylyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ); -// double discounted_strike = strike * Math.exp(-1.0 * riskFreeInterestRate * daysToExpiration); -// double probabiltity_weighted_value_of_discounted_strike = discounted_strike * normalSDist( -1.0 * d2 ); -// -// double d1 = d2 + ( sigma * Math.sqrt(daysToExpiration) ); -// double discounted_underlying = underylyingPrice * Math.exp(-1.0 * dividendYield * daysToExpiration); -// double probability_weighted_value_of_being_exercised = discounted_underlying * normalSDist( -1.0 * d1 ); -// -// return probabiltity_weighted_value_of_discounted_strike - probability_weighted_value_of_being_exercised; -// } -// -// private double getDaysToExpiration(intrinio.realtime.options.Trade latestOptionTrade, intrinio.realtime.options.Quote latestOptionQuote){ -// double latestActivity = Math.max(latestOptionTrade.timestamp(), latestOptionQuote.timestamp()); -// long expirationAsUnixWholeSeconds = latestOptionTrade.getExpirationDate().toEpochSecond(); -// double fractional = ((double)(latestOptionTrade.getExpirationDate().getNano())) / 1_000_000_000.0; -// double expiration = (((double)expirationAsUnixWholeSeconds) + fractional); -// return (expiration - latestActivity) / 86400.0; //86400 is seconds in a day -// } -// -// private Greek getGreek(SecurityData calcData, Double riskFreeInterestRate, String contract, double daysToExpiration, double marketPrice, double impliedVolatility, double delta, double gamma, double theta, double vega){ -// return new Greek(calcData.getTickerSymbol(), -// contract, -// calcData.getEquitiesTrade(), -// calcData.getAllOptionsContractData().get(contract).getTrade(),//latestOptionTrade, -// calcData.getAllOptionsContractData().get(contract).getQuote(),//latestOptionQuote, -// riskFreeInterestRate, -// calcData.getSupplementaryDatum(DividendYieldKey), -// daysToExpiration, -// marketPrice, -// impliedVolatility, -// delta, -// gamma, -// theta, -// vega, -// GreekCalculationMethod.BLACK_SCHOLES); -// } -//} +package intrinio.realtime.composite; + +import java.util.Date; + +public class BlackScholesGreekCalculator { + private static final double LOW_VOL = 0.0D; + private static final double HIGH_VOL = 5.0D; + private static final double VOL_TOLERANCE = 1e-12D; + private static final double MIN_Z_SCORE = -8.0D; + private static final double MAX_Z_SCORE = 8.0D; + private static final double root2Pi = Math.sqrt(2.0D * Math.PI); + + public static Greek calculate(double riskFreeInterestRate, double dividendYield, double underlyingPrice, double latestEventUnixTimestamp, double marketPrice, boolean isPut, double strike, Date expirationDate) { + if (marketPrice <= 0.0D || riskFreeInterestRate <= 0.0D || underlyingPrice <= 0.0D) + return new Greek(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, false); + + double yearsToExpiration = getYearsToExpiration(latestEventUnixTimestamp, expirationDate); + + if (yearsToExpiration <= 0.0D || strike <= 0.0D) + return new Greek(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, false); + + double impliedVolatility = calcImpliedVolatility(isPut, underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, dividendYield, marketPrice); + if (impliedVolatility == 0.0D) + return new Greek(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, false); + + // Compute common values once for all Greeks to avoid redundant calcs + double sqrtT = Math.sqrt(yearsToExpiration); + double d1 = d1(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, impliedVolatility, dividendYield); + double d2 = d1 - impliedVolatility * sqrtT; + double expQt = Math.exp(-dividendYield * yearsToExpiration); + double expRt = Math.exp(-riskFreeInterestRate * yearsToExpiration); + double nD1 = cumulativeNormalDistribution(d1); + double nD2 = cumulativeNormalDistribution(d2); + double phiD1 = normalPdf(d1); + + double delta = isPut ? expQt * (nD1 - 1.0D) : expQt * nD1; + double gamma = expQt * phiD1 / (underlyingPrice * impliedVolatility * sqrtT); + double vega = 0.01D * underlyingPrice * expQt * sqrtT * phiD1; + + // Theta with correct dividend adjustments + double term1 = expQt * underlyingPrice * phiD1 * impliedVolatility / (2.0D * sqrtT); + double term2 = riskFreeInterestRate * strike * expRt * (isPut ? (1.0D - nD2) : nD2); + double term3 = dividendYield * underlyingPrice * expQt * (isPut ? (1.0D - nD1) : nD1); + double theta = isPut ? (-term1 + term2 - term3) / 365.25D : (-term1 - term2 + term3) / 365.25D; + + return new Greek(impliedVolatility, delta, gamma, theta, vega, true); + } + + private static double calcImpliedVolatility(boolean isPut, double underlyingPrice, double strike, double yearsToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice) { + double tol = 1e-10D; + double forward = underlyingPrice * Math.exp((riskFreeInterestRate - dividendYield) * yearsToExpiration); + double m = forward / strike; + double sigma = Math.sqrt(2.0D * Math.abs(Math.log(m)) / yearsToExpiration); + if (Double.isNaN(sigma) || sigma <= 0.0D) sigma = 0.3D; + + int maxIter = 50; + for (int iter = 0; iter < maxIter; iter++) { + double price = isPut ? calcPricePut(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, sigma, dividendYield) : calcPriceCall(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, sigma, dividendYield); + double diff = price - marketPrice; + if (Math.abs(diff) < tol) break; + + double d1 = d1(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, sigma, dividendYield); + double vega = underlyingPrice * Math.exp(-dividendYield * yearsToExpiration) * Math.sqrt(yearsToExpiration) * normalPdf(d1); + if (Math.abs(vega) < 1e-10D) break; // avoid division by zero + + sigma -= diff / vega; + if (sigma <= 0.0D) sigma = 0.0001D; // prevent negative or zero + } + + return sigma; + } + + private static double d1(double underlyingPrice, double strike, double yearsToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) { + double numerator = Math.log(underlyingPrice / strike) + (riskFreeInterestRate - dividendYield + 0.5D * sigma * sigma) * yearsToExpiration; + double denominator = sigma * Math.sqrt(yearsToExpiration); + return numerator / denominator; + } + + private static double d2(double underlyingPrice, double strike, double yearsToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) { + return d1(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, sigma, dividendYield) - sigma * Math.sqrt(yearsToExpiration); + } + + private static double cumulativeNormalDistribution(double z) { + if (Math.abs(z) < 1.5D) + return cumulativeNormalDistributionSeries(z); + + if (z > MAX_Z_SCORE) return 1.0D; + if (z < MIN_Z_SCORE) return 0.0D; + + boolean isNegative = z < 0.0D; + if (isNegative) z = -z; + + double t = 1.0D / (1.0D + 0.2316419D * z); + double poly = t * (0.319381530D + t * (-0.356563782D + t * (1.781477937D + t * (-1.821255978D + t * 1.330274429D)))); + + double pdf = Math.exp(-0.5D * z * z) / root2Pi; + double tail = pdf * poly; + + return isNegative ? tail : 1.0D - tail; + } + + private static double cumulativeNormalDistributionSeries(double z) { + double absZ = Math.abs(z); + double sum = 0.0D; + double term = absZ; + double i = 3.0D; + while (sum + term != sum) { + sum += term; + term = term * absZ * absZ / i; + i += 2.0D; + } + double pdf = Math.exp(-0.5D * absZ * absZ) / root2Pi; + double half = pdf * sum; + return z >= 0.0D ? 0.5D + half : 0.5D - half; + } + + private static double normalPdf(double x) { + return Math.exp(-0.5D * x * x) / root2Pi; + } + + private static double calcPriceCall(double underlyingPrice, double strike, double yearsToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) { + double d1 = d1(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, sigma, dividendYield); + double d2 = d1 - sigma * Math.sqrt(yearsToExpiration); + double discountedUnderlying = Math.exp(-dividendYield * yearsToExpiration) * underlyingPrice; + double discountedStrike = Math.exp(-riskFreeInterestRate * yearsToExpiration) * strike; + return discountedUnderlying * cumulativeNormalDistribution(d1) - discountedStrike * cumulativeNormalDistribution(d2); + } + + private static double calcPricePut(double underlyingPrice, double strike, double yearsToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) { + double d1 = d1(underlyingPrice, strike, yearsToExpiration, riskFreeInterestRate, sigma, dividendYield); + double d2 = d1 - sigma * Math.sqrt(yearsToExpiration); + double discountedUnderlying = Math.exp(-dividendYield * yearsToExpiration) * underlyingPrice; + double discountedStrike = Math.exp(-riskFreeInterestRate * yearsToExpiration) * strike; + return discountedStrike * cumulativeNormalDistribution(-d2) - discountedUnderlying * cumulativeNormalDistribution(-d1); + } + + private static double getYearsToExpiration(double latestActivityUnixTime, Date expirationDate) { + double expiration = expirationDate.getTime() / 1000.0D; + return (expiration - latestActivityUnixTime) / 31557600.0D; + } +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/CalculateNewGreek.java b/src/intrinio/realtime/composite/CalculateNewGreek.java new file mode 100644 index 0000000..c45bd6b --- /dev/null +++ b/src/intrinio/realtime/composite/CalculateNewGreek.java @@ -0,0 +1,6 @@ +package intrinio.realtime.composite; + +@FunctionalInterface +public interface CalculateNewGreek { + void calculateNewGreek(OptionsContractData optionsContractData, SecurityData securityData, DataCache dataCache); +} diff --git a/src/intrinio/realtime/composite/CurrentDataCache.java b/src/intrinio/realtime/composite/CurrentDataCache.java index 2db1a5c..d8163b2 100644 --- a/src/intrinio/realtime/composite/CurrentDataCache.java +++ b/src/intrinio/realtime/composite/CurrentDataCache.java @@ -1,256 +1,304 @@ package intrinio.realtime.composite; -import java.util.*; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -/** - * This is a simple thread-safe non-transactional cache intended to store the most recent data for an event type. - * The on-updated events do not clone data before returning, so will always represent the newest data, even if it's - * changed before you process it. - */ -public class CurrentDataCache implements DataCache -{ - //region Data Members - private final ConcurrentHashMap data; - private final Map readonlyData; - private OnSupplementalDatumUpdated onSupplementalDatumUpdated; - private OnSecuritySupplementalDatumUpdated onSecuritySupplementalDatumUpdated; - private OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated; - private OnEquitiesQuoteUpdated onEquitiesQuoteUpdated; - private OnEquitiesTradeUpdated onEquitiesTradeUpdated; - private OnOptionsQuoteUpdated onOptionsQuoteUpdated; - private OnOptionsTradeUpdated onOptionsTradeUpdated; - private OnOptionsRefreshUpdated onOptionsRefreshUpdated; - private final ConcurrentHashMap supplementaryData; - private final Map readonlySupplementaryData; - //endregion Data Members - - //region Constructors - public CurrentDataCache(){ - this.data = new ConcurrentHashMap(); - this.readonlyData = java.util.Collections.unmodifiableMap(this.data); - this.onSupplementalDatumUpdated = null; - this.onSecuritySupplementalDatumUpdated = null; - this.onOptionsContractSupplementalDatumUpdated = null; - this.onEquitiesQuoteUpdated = null; - this.onEquitiesTradeUpdated = null; - this.onOptionsQuoteUpdated = null; - this.onOptionsTradeUpdated = null; - this.onOptionsRefreshUpdated = null; - this.supplementaryData = new ConcurrentHashMap(); - this.readonlySupplementaryData = java.util.Collections.unmodifiableMap(supplementaryData); - } - //endregion Constructors - - //region Public Methods - public Double getsupplementaryDatum(String key){ +class CurrentDataCache implements DataCache { + private final ConcurrentHashMap securities = new ConcurrentHashMap<>(); + private final Map readonlySecurities = Collections.unmodifiableMap(securities); + private final ConcurrentHashMap supplementaryData = new ConcurrentHashMap<>(); + private final Map readonlySupplementaryData = Collections.unmodifiableMap(supplementaryData); + + private OnSupplementalDatumUpdated supplementalDatumUpdatedCallback; + private OnSecuritySupplementalDatumUpdated securitySupplementalDatumUpdatedCallback; + private OnOptionsContractSupplementalDatumUpdated optionsContractSupplementalDatumUpdatedCallback; + + private OnOptionsContractGreekDataUpdated optionsContractGreekDataUpdatedCallback; + + private OnEquitiesTradeUpdated equitiesTradeUpdatedCallback; + private OnEquitiesQuoteUpdated equitiesQuoteUpdatedCallback; + + private OnOptionsTradeUpdated optionsTradeUpdatedCallback; + private OnOptionsQuoteUpdated optionsQuoteUpdatedCallback; + private OnOptionsRefreshUpdated optionsRefreshUpdatedCallback; + private OnOptionsUnusualActivityUpdated optionsUnusualActivityUpdatedCallback; + + public CurrentDataCache() { + } + + public Double getSupplementaryDatum(String key) { return supplementaryData.getOrDefault(key, null); } - public boolean setsupplementaryDatum(String key, double datum){ - boolean isSet = datum == supplementaryData.compute(key, (k, oldValue) -> datum); - if (this.onSupplementalDatumUpdated != null){ - try{ - this.onSupplementalDatumUpdated.onSupplementalDatumUpdated(key, datum, this); - }catch (Exception e){ - Log("Error in setsupplementaryDatum Callback: " + e.getMessage()); + public boolean setSupplementaryDatum(String key, Double datum, SupplementalDatumUpdate update) { + Double newValue = supplementaryData.compute(key, (k, oldValue) -> update.supplementalDatumUpdate(k, oldValue, datum)); + boolean result = java.util.Objects.equals(datum, newValue); + if (result && supplementalDatumUpdatedCallback != null) { + try { + supplementalDatumUpdatedCallback.onSupplementalDatumUpdated(key, datum, this); + } catch (Exception e) { + Log("Error in OnSupplementalDatumUpdated Callback: " + e.getMessage()); } } - return isSet; + return result; } - public Map getAllSupplementaryData(){return readonlySupplementaryData;} + public Map getAllSupplementaryData() { + return readonlySupplementaryData; + } - public SecurityData getSecurityData(String tickerSymbol){ - return data.getOrDefault(tickerSymbol, null); + public Double getSecuritySupplementalDatum(String tickerSymbol, String key) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getSupplementaryDatum(key) : null; } - public Map getAllSecurityData(){ - return readonlyData; + public boolean setSecuritySupplementalDatum(String tickerSymbol, String key, Double datum, SupplementalDatumUpdate update) { + if (tickerSymbol != null && !tickerSymbol.trim().isEmpty()) { + SecurityData securityData = securities.computeIfAbsent(tickerSymbol, k -> new CurrentSecurityData(tickerSymbol, null, null, null)); + return securityData.setSupplementaryDatum(key, datum, securitySupplementalDatumUpdatedCallback, this, update); + } + return false; } - public intrinio.realtime.equities.Trade getEquityTrade(String tickerSymbol){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getEquitiesTrade(); - else return null; + public Double getOptionsContractSupplementalDatum(String tickerSymbol, String contract, String key) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractSupplementalDatum(contract, key) : null; } - public boolean setEquityTrade(intrinio.realtime.equities.Trade trade){ - String symbol = trade.symbol(); - CurrentSecurityData securityData; - if (data.containsKey(symbol)){ - securityData = data.get(symbol); - } - else { - CurrentSecurityData newData = new CurrentSecurityData(symbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(symbol, newData); - securityData = Objects.requireNonNullElse(possiblyNewerData, newData); + public boolean setOptionSupplementalDatum(String tickerSymbol, String contract, String key, Double datum, SupplementalDatumUpdate update) { + if (tickerSymbol != null && !tickerSymbol.trim().isEmpty()) { + SecurityData securityData = securities.computeIfAbsent(tickerSymbol, k -> new CurrentSecurityData(tickerSymbol, null, null, null)); + return securityData.setOptionsContractSupplementalDatum(contract, key, datum, optionsContractSupplementalDatumUpdatedCallback, this, update); } - return securityData.setEquitiesTrade(trade, this.onEquitiesTradeUpdated, this); + return false; } - public intrinio.realtime.equities.Quote getEquityQuote(String tickerSymbol){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getEquitiesQuote(); - else return null; + public Greek getOptionsContractGreekData(String tickerSymbol, String contract, String key) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractGreekData(contract, key) : null; } - public boolean setEquityQuote(intrinio.realtime.equities.Quote quote){ - String symbol = quote.symbol(); - CurrentSecurityData securityData; - if (data.containsKey(symbol)){ - securityData = data.get(symbol); + public boolean setOptionGreekData(String tickerSymbol, String contract, String key, Greek data, GreekDataUpdate update) { + if (tickerSymbol != null && !tickerSymbol.trim().isEmpty()) { + SecurityData securityData = securities.computeIfAbsent(tickerSymbol, k -> new CurrentSecurityData(tickerSymbol, null, null, null)); + return securityData.setOptionsContractGreekData(contract, key, data, optionsContractGreekDataUpdatedCallback, this, update); } - else { - CurrentSecurityData newData = new CurrentSecurityData(symbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(symbol, newData); - securityData = Objects.requireNonNullElse(possiblyNewerData, newData); - } - return securityData.setEquitiesQuote(quote, this.onEquitiesQuoteUpdated, this); + return false; } - public OptionsContractData getOptionsContractData(String tickerSymbol, String contract){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getOptionsContractData(contract); - else return null; + public SecurityData getSecurityData(String tickerSymbol) { + return securities.get(tickerSymbol); } - public intrinio.realtime.options.Trade getOptionsTrade(String tickerSymbol, String contract){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getOptionsContractTrade(contract); - else return null; + public Map getAllSecurityData() { + return readonlySecurities; } - public boolean setOptionsTrade(intrinio.realtime.options.Trade trade){ - String underlyingSymbol = trade.getUnderlyingSymbol(); - CurrentSecurityData securityData; - if (data.containsKey(underlyingSymbol)){ - securityData = data.get(underlyingSymbol); - } - else { - CurrentSecurityData newData = new CurrentSecurityData(underlyingSymbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(underlyingSymbol, newData); - securityData = Objects.requireNonNullElse(possiblyNewerData, newData); - } - return securityData.setOptionsTrade(trade, this.onOptionsTradeUpdated, this); + public OptionsContractData getOptionsContractData(String tickerSymbol, String contract) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractData(contract) : null; } - public intrinio.realtime.options.Quote getOptionsQuote(String tickerSymbol, String contract){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getOptionsContractQuote(contract); - else return null; + public Map getAllOptionsContractData(String tickerSymbol) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getAllOptionsContractData() : Collections.emptyMap(); } - public boolean setOptionsQuote(intrinio.realtime.options.Quote quote){ - String underlyingSymbol = quote.getUnderlyingSymbol(); - CurrentSecurityData securityData; - if (data.containsKey(underlyingSymbol)){ - securityData = data.get(underlyingSymbol); - } - else { - CurrentSecurityData newData = new CurrentSecurityData(underlyingSymbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(underlyingSymbol, newData); - securityData = Objects.requireNonNullElse(possiblyNewerData, newData); + public intrinio.realtime.equities.Trade getLatestEquityTrade(String tickerSymbol) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getLatestEquitiesTrade() : null; + } + + public boolean setEquityTrade(intrinio.realtime.equities.Trade trade) { + if (trade != null) { + String symbol = trade.symbol(); + SecurityData securityData = securities.computeIfAbsent(symbol, k -> new CurrentSecurityData(symbol, trade, null, null)); + return securityData.setEquitiesTrade(trade, equitiesTradeUpdatedCallback, this); } - return securityData.setOptionsQuote(quote, this.onOptionsQuoteUpdated, this); + return false; } - public intrinio.realtime.options.Refresh getOptionsRefresh(String tickerSymbol, String contract){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getOptionsContractRefresh(contract); - else return null; + public void onTrade(intrinio.realtime.equities.Trade trade) { + setEquityTrade(trade); } - public boolean setOptionsRefresh(intrinio.realtime.options.Refresh refresh){ - String underlyingSymbol = refresh.getUnderlyingSymbol(); - CurrentSecurityData securityData; - if (data.containsKey(underlyingSymbol)){ - securityData = data.get(underlyingSymbol); - } - else { - CurrentSecurityData newData = new CurrentSecurityData(underlyingSymbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(underlyingSymbol, newData); - securityData = Objects.requireNonNullElse(possiblyNewerData, newData); + public intrinio.realtime.equities.Quote getLatestEquityAskQuote(String tickerSymbol) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getLatestEquitiesAskQuote() : null; + } + + public intrinio.realtime.equities.Quote getLatestEquityBidQuote(String tickerSymbol) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getLatestEquitiesBidQuote() : null; + } + + public boolean setEquityQuote(intrinio.realtime.equities.Quote quote) { + if (quote != null) { + String symbol = quote.symbol(); + SecurityData securityData = securities.computeIfAbsent(symbol, k -> new CurrentSecurityData(symbol, null, quote.type() == intrinio.realtime.equities.QuoteType.ASK ? quote : null, quote.type() == intrinio.realtime.equities.QuoteType.BID ? quote : null)); + return securityData.setEquitiesQuote(quote, equitiesQuoteUpdatedCallback, this); } - return securityData.setOptionsRefresh(refresh, this.onOptionsRefreshUpdated, this); + return false; + } + + public void onQuote(intrinio.realtime.equities.Quote quote) { + setEquityQuote(quote); } - public Double getSecuritySupplementalDatum(String tickerSymbol, String key){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getSupplementaryDatum(key); - else return null; + public intrinio.realtime.options.Trade getLatestOptionsTrade(String tickerSymbol, String contract) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractTrade(contract) : null; } - public boolean setSecuritySupplementalDatum(String tickerSymbol, String key, double datum){ - boolean result = false; - CurrentSecurityData currentSecurityData; - if (data.containsKey(tickerSymbol)) { - currentSecurityData = data.get(tickerSymbol); + public boolean setOptionsTrade(intrinio.realtime.options.Trade trade) { + if (trade != null) { + String underlyingSymbol = trade.getUnderlyingSymbol(); + SecurityData securityData = securities.computeIfAbsent(underlyingSymbol, k -> new CurrentSecurityData(underlyingSymbol, null, null, null)); + return securityData.setOptionsContractTrade(trade, optionsTradeUpdatedCallback, this); } - else { - CurrentSecurityData newData = new CurrentSecurityData(tickerSymbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(tickerSymbol, newData); - currentSecurityData = possiblyNewerData == null ? newData : possiblyNewerData; + return false; + } + + public void onTrade(intrinio.realtime.options.Trade trade) { + setOptionsTrade(trade); + } + + public intrinio.realtime.options.Quote getLatestOptionsQuote(String tickerSymbol, String contract) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractQuote(contract) : null; + } + + public boolean setOptionsQuote(intrinio.realtime.options.Quote quote) { + if (quote != null) { + String underlyingSymbol = quote.getUnderlyingSymbol(); + SecurityData securityData = securities.computeIfAbsent(underlyingSymbol, k -> new CurrentSecurityData(underlyingSymbol, null, null, null)); + return securityData.setOptionsContractQuote(quote, optionsQuoteUpdatedCallback, this); } - return currentSecurityData.setSupplementaryDatum(key, datum, this.onSecuritySupplementalDatumUpdated, this); + return false; + } + + public void onQuote(intrinio.realtime.options.Quote quote) { + setOptionsQuote(quote); } - public Double getOptionsContractSupplementalDatum(String tickerSymbol, String contract, String key){ - if (data.containsKey(tickerSymbol)) - return data.get(tickerSymbol).getOptionsContractSupplementalDatum(contract, key); - else return null; + public intrinio.realtime.options.Refresh getLatestOptionsRefresh(String tickerSymbol, String contract) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractRefresh(contract) : null; } - public boolean setOptionSupplementalDatum(String tickerSymbol, String contract, String key, double datum){ - CurrentSecurityData currentSecurityData; - if (data.containsKey(tickerSymbol)) { - currentSecurityData = data.get(tickerSymbol); + public boolean setOptionsRefresh(intrinio.realtime.options.Refresh refresh) { + if (refresh != null) { + String underlyingSymbol = refresh.getUnderlyingSymbol(); + SecurityData securityData = securities.computeIfAbsent(underlyingSymbol, k -> new CurrentSecurityData(underlyingSymbol, null, null, null)); + return securityData.setOptionsContractRefresh(refresh, optionsRefreshUpdatedCallback, this); } - else { - CurrentSecurityData newData = new CurrentSecurityData(tickerSymbol); - CurrentSecurityData possiblyNewerData = data.putIfAbsent(tickerSymbol, newData); - currentSecurityData = possiblyNewerData == null ? newData : possiblyNewerData; + return false; + } + + public void onRefresh(intrinio.realtime.options.Refresh refresh) { + setOptionsRefresh(refresh); + } + + public intrinio.realtime.options.UnusualActivity getLatestOptionsUnusualActivity(String tickerSymbol, String contract) { + SecurityData securityData = securities.get(tickerSymbol); + return securityData != null ? securityData.getOptionsContractUnusualActivity(contract) : null; + } + + public boolean setOptionsUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity) { + if (unusualActivity != null) { + String underlyingSymbol = unusualActivity.getUnderlyingSymbol(); + SecurityData securityData = securities.computeIfAbsent(underlyingSymbol, k -> new CurrentSecurityData(underlyingSymbol, null, null, null)); + return securityData.setOptionsContractUnusualActivity(unusualActivity, optionsUnusualActivityUpdatedCallback, this); } - return currentSecurityData.setOptionsContractSupplementalDatum(contract, key, datum, onOptionsContractSupplementalDatumUpdated, this); + return false; + } + + public void onUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity) { + setOptionsUnusualActivity(unusualActivity); + } + + public OnSupplementalDatumUpdated getSupplementalDatumUpdatedCallback() { + return supplementalDatumUpdatedCallback; + } + + public void setSupplementalDatumUpdatedCallback(OnSupplementalDatumUpdated supplementalDatumUpdatedCallback) { + this.supplementalDatumUpdatedCallback = supplementalDatumUpdatedCallback; + } + + public OnSecuritySupplementalDatumUpdated getSecuritySupplementalDatumUpdatedCallback() { + return securitySupplementalDatumUpdatedCallback; + } + + public void setSecuritySupplementalDatumUpdatedCallback(OnSecuritySupplementalDatumUpdated securitySupplementalDatumUpdatedCallback) { + this.securitySupplementalDatumUpdatedCallback = securitySupplementalDatumUpdatedCallback; } - public void setOnSupplementalDatumUpdated(OnSupplementalDatumUpdated onSupplementalDatumUpdated){ - this.onSupplementalDatumUpdated = onSupplementalDatumUpdated; + public OnOptionsContractSupplementalDatumUpdated getOptionsContractSupplementalDatumUpdatedCallback() { + return optionsContractSupplementalDatumUpdatedCallback; } - public void setOnSecuritySupplementalDatumUpdated(OnSecuritySupplementalDatumUpdated onSecuritySupplementalDatumUpdated){ - this.onSecuritySupplementalDatumUpdated = onSecuritySupplementalDatumUpdated; + public void setOptionsContractSupplementalDatumUpdatedCallback(OnOptionsContractSupplementalDatumUpdated optionsContractSupplementalDatumUpdatedCallback) { + this.optionsContractSupplementalDatumUpdatedCallback = optionsContractSupplementalDatumUpdatedCallback; } - public void setOnOptionSupplementalDatumUpdated(OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated){ - this.onOptionsContractSupplementalDatumUpdated = onOptionsContractSupplementalDatumUpdated; + public OnOptionsContractGreekDataUpdated getOptionsContractGreekDataUpdatedCallback() { + return optionsContractGreekDataUpdatedCallback; } - public void setOnEquitiesQuoteUpdated(OnEquitiesQuoteUpdated onEquitiesQuoteUpdated){ - this.onEquitiesQuoteUpdated = onEquitiesQuoteUpdated; + public void setOptionsContractGreekDataUpdatedCallback(OnOptionsContractGreekDataUpdated optionsContractGreekDataUpdatedCallback) { + this.optionsContractGreekDataUpdatedCallback = optionsContractGreekDataUpdatedCallback; } - public void setOnEquitiesTradeUpdated(OnEquitiesTradeUpdated onEquitiesTradeUpdated){ - this.onEquitiesTradeUpdated = onEquitiesTradeUpdated; + public OnEquitiesTradeUpdated getEquitiesTradeUpdatedCallback() { + return equitiesTradeUpdatedCallback; } - public void setOnOptionsQuoteUpdated(OnOptionsQuoteUpdated onOptionsQuoteUpdated){ - this.onOptionsQuoteUpdated = onOptionsQuoteUpdated; + public void setEquitiesTradeUpdatedCallback(OnEquitiesTradeUpdated equitiesTradeUpdatedCallback) { + this.equitiesTradeUpdatedCallback = equitiesTradeUpdatedCallback; } - public void setOnOptionsTradeUpdated(OnOptionsTradeUpdated onOptionsTradeUpdated){ - this.onOptionsTradeUpdated = onOptionsTradeUpdated; + public OnEquitiesQuoteUpdated getEquitiesQuoteUpdatedCallback() { + return equitiesQuoteUpdatedCallback; } - public void setOnOptionsRefreshUpdated(OnOptionsRefreshUpdated onOptionsRefreshUpdated){ - this.onOptionsRefreshUpdated = onOptionsRefreshUpdated; + public void setEquitiesQuoteUpdatedCallback(OnEquitiesQuoteUpdated equitiesQuoteUpdatedCallback) { + this.equitiesQuoteUpdatedCallback = equitiesQuoteUpdatedCallback; } - //endregion Public Methods - //region Private Methods + public OnOptionsTradeUpdated getOptionsTradeUpdatedCallback() { + return optionsTradeUpdatedCallback; + } + + public void setOptionsTradeUpdatedCallback(OnOptionsTradeUpdated optionsTradeUpdatedCallback) { + this.optionsTradeUpdatedCallback = optionsTradeUpdatedCallback; + } + + public OnOptionsQuoteUpdated getOptionsQuoteUpdatedCallback() { + return optionsQuoteUpdatedCallback; + } + + public void setOptionsQuoteUpdatedCallback(OnOptionsQuoteUpdated optionsQuoteUpdatedCallback) { + this.optionsQuoteUpdatedCallback = optionsQuoteUpdatedCallback; + } + + public OnOptionsRefreshUpdated getOptionsRefreshUpdatedCallback() { + return optionsRefreshUpdatedCallback; + } + + public void setOptionsRefreshUpdatedCallback(OnOptionsRefreshUpdated optionsRefreshUpdatedCallback) { + this.optionsRefreshUpdatedCallback = optionsRefreshUpdatedCallback; + } + + public OnOptionsUnusualActivityUpdated getOptionsUnusualActivityUpdatedCallback() { + return optionsUnusualActivityUpdatedCallback; + } + + public void setOptionsUnusualActivityUpdatedCallback(OnOptionsUnusualActivityUpdated optionsUnusualActivityUpdatedCallback) { + this.optionsUnusualActivityUpdatedCallback = optionsUnusualActivityUpdatedCallback; + } + private void Log(String message){ System.out.println(message); } - - //endregion Private Methods -} +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/CurrentOptionsContractData.java b/src/intrinio/realtime/composite/CurrentOptionsContractData.java index 23e051d..bb93d2b 100644 --- a/src/intrinio/realtime/composite/CurrentOptionsContractData.java +++ b/src/intrinio/realtime/composite/CurrentOptionsContractData.java @@ -1,126 +1,209 @@ package intrinio.realtime.composite; -import java.util.Map; +import intrinio.realtime.options.Trade; +import intrinio.realtime.options.Quote; +import intrinio.realtime.options.Refresh; +import intrinio.realtime.options.UnusualActivity; +import intrinio.realtime.options.QuoteType; import java.util.concurrent.ConcurrentHashMap; +import java.util.Collections; +import java.util.Map; -public class CurrentOptionsContractData implements OptionsContractData { +/** + * Not for Use yet. Subject to change. + */ +class CurrentOptionsContractData implements OptionsContractData { private final String contract; - private volatile intrinio.realtime.options.Trade latestTrade; - private volatile intrinio.realtime.options.Quote latestQuote; - private volatile intrinio.realtime.options.Refresh latestRefresh; - private final ConcurrentHashMap supplementaryData; - private final Map readonlySupplementaryData; - - public CurrentOptionsContractData(String contract, intrinio.realtime.options.Trade latestTrade, intrinio.realtime.options.Quote latestQuote, intrinio.realtime.options.Refresh latestRefresh){ + private Trade latestTrade; + private Quote latestQuote; + private Refresh latestRefresh; + private UnusualActivity latestUnusualActivity; + private final ConcurrentHashMap supplementaryData = new ConcurrentHashMap<>(); + private final Map readonlySupplementaryData = Collections.unmodifiableMap(supplementaryData); + private final ConcurrentHashMap greekData = new ConcurrentHashMap<>(); + private final Map readonlyGreekData = Collections.unmodifiableMap(greekData); + + public CurrentOptionsContractData(String contract, + Trade latestTrade, + Quote latestQuote, + Refresh latestRefresh, + UnusualActivity latestUnusualActivity) { this.contract = contract; this.latestTrade = latestTrade; this.latestQuote = latestQuote; this.latestRefresh = latestRefresh; - this.supplementaryData = new ConcurrentHashMap(); - this.readonlySupplementaryData = java.util.Collections.unmodifiableMap(supplementaryData); + this.latestUnusualActivity = latestUnusualActivity; } - public String getContract(){ + @Override + public String getContract() { return this.contract; } - public intrinio.realtime.options.Trade getTrade(){ + @Override + public Trade getLatestTrade() { return this.latestTrade; } - public intrinio.realtime.options.Quote getQuote(){ + @Override + public Quote getLatestQuote() { return this.latestQuote; } - public intrinio.realtime.options.Refresh getRefresh(){ + @Override + public Refresh getLatestRefresh() { return this.latestRefresh; } - public boolean setTrade(intrinio.realtime.options.Trade trade){ + @Override + public UnusualActivity getLatestUnusualActivity() { + return this.latestUnusualActivity; + } + + @Override + public boolean setTrade(Trade trade) { //dirty set - if ((latestTrade == null) || (trade.timestamp() > latestTrade.timestamp())) { - latestTrade = trade; + if (this.latestTrade == null || (trade != null && trade.timestamp() > this.latestTrade.timestamp())) { + this.latestTrade = trade; return true; } return false; } - boolean setTrade(intrinio.realtime.options.Trade trade, OnOptionsTradeUpdated onOptionsTradeUpdated, SecurityData securityData, DataCache dataCache){ - boolean isSet = this.setTrade(trade); - if (isSet && onOptionsTradeUpdated != null){ - try{ - onOptionsTradeUpdated.onOptionsTradeUpdated(this, dataCache, securityData); - }catch (Exception e){ - Log("Error in onOptionsTradeUpdated Callback: " + e.getMessage()); + @Override + public boolean setTrade(Trade trade, OnOptionsTradeUpdated onOptionsTradeUpdated, SecurityData securityData, DataCache dataCache) { + boolean isSet = setTrade(trade); + if (isSet && onOptionsTradeUpdated != null) { + try { + onOptionsTradeUpdated.onOptionsTradeUpdated(this, dataCache, securityData, trade); + } catch (Exception e) { + Log("Error in OnOptionsTradeUpdated Callback: " + e.getMessage()); } } return isSet; } - public boolean setQuote(intrinio.realtime.options.Quote quote){ + @Override + public boolean setQuote(Quote quote) { //dirty set - if ((latestQuote == null) || (quote.timestamp() > latestQuote.timestamp())) { - latestQuote = quote; + if (this.latestQuote == null || (quote != null && quote.timestamp() > this.latestQuote.timestamp())) { + this.latestQuote = quote; return true; } return false; } - boolean setQuote(intrinio.realtime.options.Quote quote, OnOptionsQuoteUpdated onOptionsQuoteUpdated, SecurityData securityData, DataCache dataCache){ + @Override + public boolean setQuote(Quote quote, OnOptionsQuoteUpdated onOptionsQuoteUpdated, SecurityData securityData, DataCache dataCache) { boolean isSet = this.setQuote(quote); - if (isSet && onOptionsQuoteUpdated != null){ - try{ - onOptionsQuoteUpdated.onOptionsQuoteUpdated(this, dataCache, securityData); - }catch (Exception e){ + if (isSet && onOptionsQuoteUpdated != null) { + try { + onOptionsQuoteUpdated.onOptionsQuoteUpdated(this, dataCache, securityData, quote); + } catch (Exception e) { Log("Error in onOptionsQuoteUpdated Callback: " + e.getMessage()); } } return isSet; } - public boolean setRefresh(intrinio.realtime.options.Refresh refresh){ - latestRefresh = refresh; + @Override + public boolean setRefresh(Refresh refresh) { + this.latestRefresh = refresh; return true; } - boolean setRefresh(intrinio.realtime.options.Refresh refresh, OnOptionsRefreshUpdated onOptionsRefreshUpdated, SecurityData securityData, DataCache dataCache){ + @Override + public boolean setRefresh(Refresh refresh, OnOptionsRefreshUpdated onOptionsRefreshUpdated, SecurityData securityData, DataCache dataCache) { boolean isSet = this.setRefresh(refresh); - if (isSet && onOptionsRefreshUpdated != null){ - try{ - onOptionsRefreshUpdated.onOptionsRefreshUpdated(this, dataCache, securityData); - }catch (Exception e){ + if (isSet && onOptionsRefreshUpdated != null) { + try { + onOptionsRefreshUpdated.onOptionsRefreshUpdated(this, dataCache, securityData, refresh); + } catch (Exception e) { Log("Error in onOptionsRefreshUpdated Callback: " + e.getMessage()); } } return isSet; } - public Double getSupplementaryDatum(String key){ + @Override + public boolean setUnusualActivity(UnusualActivity unusualActivity) { + this.latestUnusualActivity = unusualActivity; + return true; + } + + @Override + public boolean setUnusualActivity(UnusualActivity unusualActivity, OnOptionsUnusualActivityUpdated onOptionsUnusualActivityUpdated, SecurityData securityData, DataCache dataCache) { + boolean isSet = this.setUnusualActivity(unusualActivity); + if (isSet && onOptionsUnusualActivityUpdated != null) { + try { + onOptionsUnusualActivityUpdated.onOptionsUnusualActivityUpdated(this, dataCache, securityData, unusualActivity); + } catch (Exception e) { + Log("Error in onOptionsUnusualActivityUpdated Callback: " + e.getMessage()); + } + } + return isSet; + } + + @Override + public Double getSupplementaryDatum(String key) { return supplementaryData.getOrDefault(key, null); } - public boolean setSupplementaryDatum(String key, double datum){ - return datum == supplementaryData.compute(key, (k, oldValue) -> datum); + @Override + public boolean setSupplementaryDatum(String key, Double datum, SupplementalDatumUpdate update) { + Double newValue = supplementaryData.compute(key, (k, oldValue) -> update.supplementalDatumUpdate(k, oldValue, datum)); + return java.util.Objects.equals(datum, newValue); } - boolean setSupplementaryDatum(String key, double datum, OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated, SecurityData securityData, DataCache dataCache){ - boolean result = setSupplementaryDatum(key, datum); - if (result && onOptionsContractSupplementalDatumUpdated != null){ - try{ + @Override + public boolean setSupplementaryDatum(String key, Double datum, OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated, SecurityData securityData, DataCache dataCache, SupplementalDatumUpdate update) { + boolean result = setSupplementaryDatum(key, datum, update); + if (result && onOptionsContractSupplementalDatumUpdated != null) { + try { onOptionsContractSupplementalDatumUpdated.onOptionsContractSupplementalDatumUpdated(key, datum, this, securityData, dataCache); - }catch (Exception e){ + } catch (Exception e) { Log("Error in onOptionsContractSupplementalDatumUpdated Callback: " + e.getMessage()); } } return result; } - public Map getAllSupplementaryData(){return readonlySupplementaryData;} + @Override + public Map getAllSupplementaryData() { + return readonlySupplementaryData; + } + + @Override + public Greek getGreekData(String key) { + return greekData.getOrDefault(key, null); + } + + @Override + public boolean setGreekData(String key, Greek datum, GreekDataUpdate update) { + Greek newValue = greekData.compute(key, (k, oldValue) -> update.greekDataUpdate(k, oldValue, datum)); + return (newValue != null && datum != null && newValue.equals(datum)) + || (newValue == null && datum == null); + } + + @Override + public boolean setGreekData(String key, Greek datum, OnOptionsContractGreekDataUpdated onOptionsContractGreekDataUpdated, SecurityData securityData, DataCache dataCache, GreekDataUpdate update) { + boolean result = setGreekData(key, datum, update); + if (result && onOptionsContractGreekDataUpdated != null) { + try { + onOptionsContractGreekDataUpdated.onOptionsContractGreekDataUpdated(key, datum, this, securityData, dataCache); + } catch (Exception e) { + Log("Error in onOptionsContractGreekDataUpdated Callback: " + e.getMessage()); + } + } + return result; + } + + @Override + public Map getAllGreekData() { + return readonlyGreekData; + } - //region Private Methods private void Log(String message){ System.out.println(message); } - - //endregion Private Methods -} +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/CurrentSecurityData.java b/src/intrinio/realtime/composite/CurrentSecurityData.java index 6f6a939..687f141 100644 --- a/src/intrinio/realtime/composite/CurrentSecurityData.java +++ b/src/intrinio/realtime/composite/CurrentSecurityData.java @@ -1,254 +1,352 @@ package intrinio.realtime.composite; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -public class CurrentSecurityData implements SecurityData{ +class CurrentSecurityData implements SecurityData { private final String tickerSymbol; - private volatile intrinio.realtime.equities.Trade equitiesTrade; - private volatile intrinio.realtime.equities.Quote equitiesQuote; - private final ConcurrentHashMap contracts; - private final Map readonlyContracts; - private final ConcurrentHashMap supplementaryData; - private final Map readonlySupplementaryData; - - public CurrentSecurityData(String tickerSymbol){ + private intrinio.realtime.equities.Trade latestTrade; + private intrinio.realtime.equities.Quote latestAskQuote; + private intrinio.realtime.equities.Quote latestBidQuote; + private final ConcurrentHashMap contracts = new ConcurrentHashMap<>(); + private final Map readonlyContracts = Collections.unmodifiableMap(contracts); + private final ConcurrentHashMap supplementaryData = new ConcurrentHashMap<>(); + private final Map readonlySupplementaryData = Collections.unmodifiableMap(supplementaryData); + + public CurrentSecurityData(String tickerSymbol, + intrinio.realtime.equities.Trade latestTrade, + intrinio.realtime.equities.Quote latestAskQuote, + intrinio.realtime.equities.Quote latestBidQuote) { this.tickerSymbol = tickerSymbol; - this.contracts = new ConcurrentHashMap(); - this.readonlyContracts = java.util.Collections.unmodifiableMap(contracts); - this.supplementaryData = new ConcurrentHashMap(); - this.readonlySupplementaryData = java.util.Collections.unmodifiableMap(supplementaryData); + this.latestTrade = latestTrade; + this.latestAskQuote = latestAskQuote; + this.latestBidQuote = latestBidQuote; } - public String getTickerSymbol(){ + @Override + public String getTickerSymbol() { return tickerSymbol; } - public Double getSupplementaryDatum(String key){ - return supplementaryData.getOrDefault(key, null); + @Override + public intrinio.realtime.equities.Trade getLatestEquitiesTrade() { + return latestTrade; } - public boolean setSupplementaryDatum(String key, double datum){ - return datum == supplementaryData.compute(key, (k, oldValue) -> datum); + @Override + public intrinio.realtime.equities.Quote getLatestEquitiesAskQuote() { + return latestAskQuote; } - boolean setSupplementaryDatum(String key, double datum, OnSecuritySupplementalDatumUpdated onSecuritySupplementalDatumUpdated, CurrentDataCache currentDataCache){ - boolean result = setSupplementaryDatum(key, datum); - if (result && onSecuritySupplementalDatumUpdated != null){ - try{ - onSecuritySupplementalDatumUpdated.onSecuritySupplementalDatumUpdated(key, datum, this, currentDataCache); - }catch (Exception e){ - Log("Error in onSecuritySupplementalDatumUpdated Callback: " + e.getMessage()); - } - } - return result; - } - - public Map getAllSupplementaryData(){return readonlySupplementaryData;} - - public intrinio.realtime.equities.Trade getEquitiesTrade(){ - return equitiesTrade; + @Override + public intrinio.realtime.equities.Quote getLatestEquitiesBidQuote() { + return latestBidQuote; } - public intrinio.realtime.equities.Quote getEquitiesQuote(){ - return equitiesQuote; + @Override + public Double getSupplementaryDatum(String key) { + return supplementaryData.getOrDefault(key, null); } - public OptionsContractData getOptionsContractData(String contract){ - return contracts.getOrDefault(contract, null); + @Override + public boolean setSupplementaryDatum(String key, Double datum, SupplementalDatumUpdate update) { + Double newValue = supplementaryData.compute(key, (k, oldValue) -> update.supplementalDatumUpdate(k, oldValue, datum)); + return java.util.Objects.equals(datum, newValue); } - public Map getAllOptionsContractData(){ - return readonlyContracts; + @Override + public boolean setSupplementaryDatum(String key, Double datum, OnSecuritySupplementalDatumUpdated onSecuritySupplementalDatumUpdated, DataCache dataCache, SupplementalDatumUpdate update) { + boolean result = setSupplementaryDatum(key, datum, update); + if (result && onSecuritySupplementalDatumUpdated != null) { + try { + onSecuritySupplementalDatumUpdated.onSecuritySupplementalDatumUpdated(key, datum, this, dataCache); + } catch (Exception e) { + Log("Error in onSecuritySupplementalDatumUpdated Callback: " + e.getMessage()); + } + } + return result; } - public List getContractNames(String ticker){ - return contracts.values().stream().map(CurrentOptionsContractData::getContract).collect(Collectors.toList()); + @Override + public Map getAllSupplementaryData() { + return readonlySupplementaryData; } - public boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade){ + @Override + public boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade) { //dirty set - if ((equitiesTrade == null) || (trade.timestamp() > equitiesTrade.timestamp())) { - equitiesTrade = trade; + if (this.latestTrade == null || (trade != null && trade.timestamp() > this.latestTrade.timestamp())) { + this.latestTrade = trade; return true; } return false; } - boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade, OnEquitiesTradeUpdated onEquitiesTradeUpdated, DataCache dataCache){ - boolean isSet = this.setEquitiesTrade(trade); - if (isSet && onEquitiesTradeUpdated != null){ - try{ - onEquitiesTradeUpdated.onEquitiesTradeUpdated(this, dataCache); - }catch (Exception e){ + @Override + public boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade, OnEquitiesTradeUpdated onEquitiesTradeUpdated, DataCache dataCache) { + boolean isSet = setEquitiesTrade(trade); + if (isSet && onEquitiesTradeUpdated != null) { + try { + onEquitiesTradeUpdated.onEquitiesTradeUpdated(this, dataCache, trade); + } catch (Exception e) { Log("Error in onEquitiesTradeUpdated Callback: " + e.getMessage()); } } return isSet; } - public boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote){ - //dirty set - if ((equitiesQuote == null) || (quote.timestamp() > equitiesQuote.timestamp())) { - equitiesQuote = quote; - return true; + @Override + public boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote) { + if (quote != null) { + if (quote.type() == intrinio.realtime.equities.QuoteType.ASK) { + if (this.latestAskQuote == null || (quote.timestamp() > this.latestAskQuote.timestamp())) { + this.latestAskQuote = quote; + return true; + } + return false; + } else { // Bid + if (this.latestBidQuote == null || (quote.timestamp() > this.latestBidQuote.timestamp())) { + this.latestBidQuote = quote; + return true; + } + return false; + } } return false; } - boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote, OnEquitiesQuoteUpdated onEquitiesQuoteUpdated, DataCache dataCache){ + @Override + public boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote, OnEquitiesQuoteUpdated onEquitiesQuoteUpdated, DataCache dataCache) { boolean isSet = this.setEquitiesQuote(quote); - if (isSet && onEquitiesQuoteUpdated != null){ - try{ - onEquitiesQuoteUpdated.onEquitiesQuoteUpdated(this, dataCache); - }catch (Exception e){ + if (isSet && onEquitiesQuoteUpdated != null) { + try { + onEquitiesQuoteUpdated.onEquitiesQuoteUpdated(this, dataCache, quote); + } catch (Exception e) { Log("Error in onEquitiesQuoteUpdated Callback: " + e.getMessage()); } } return isSet; } - public intrinio.realtime.options.Trade getOptionsContractTrade(String contract){ - if (contracts.containsKey(contract)) - return contracts.get(contract).getTrade(); - else return null; + @Override + public OptionsContractData getOptionsContractData(String contract) { + return contracts.get(contract); } - public boolean setOptionsContractTrade(intrinio.realtime.options.Trade trade){ - //dirty set - if (contracts.containsKey(trade.contract())){ - return contracts.get(trade.contract()).setTrade(trade); - } - else{ - CurrentOptionsContractData data = new CurrentOptionsContractData(trade.contract(), trade, null, null); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(trade.contract(), data); - if (possiblyNewerData != null) - return possiblyNewerData.setTrade(trade); - return true; - } + @Override + public Map getAllOptionsContractData() { + return readonlyContracts; } - public boolean setOptionsTrade(intrinio.realtime.options.Trade trade, OnOptionsTradeUpdated onOptionsTradeUpdated, DataCache dataCache){ - CurrentOptionsContractData currentOptionsContractData; - String contract = trade.contract(); - if (contracts.containsKey(contract)) { - currentOptionsContractData = contracts.get(contract); + public List getContractNames() { + return contracts.values().stream().map(OptionsContractData::getContract).collect(Collectors.toList()); + } + + @Override + public intrinio.realtime.options.Trade getOptionsContractTrade(String contract) { + OptionsContractData optionsContractData = contracts.get(contract); + return optionsContractData != null ? optionsContractData.getLatestTrade() : null; + } + + @Override + public boolean setOptionsContractTrade(intrinio.realtime.options.Trade trade) { + if (trade != null) { + String contract = trade.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + CurrentOptionsContractData newDatum = new CurrentOptionsContractData(contract, trade, null, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setTrade(trade); } - else { - CurrentOptionsContractData newData = new CurrentOptionsContractData(contract, trade, null, null); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(contract, newData); - currentOptionsContractData = possiblyNewerData == null ? newData : possiblyNewerData; + return false; + } + + @Override + public boolean setOptionsContractTrade(intrinio.realtime.options.Trade trade, OnOptionsTradeUpdated onOptionsTradeUpdated, DataCache dataCache) { + if (trade != null) { + String contract = trade.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, trade, null, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setTrade(trade, onOptionsTradeUpdated, this, dataCache); } - return currentOptionsContractData.setTrade(trade, onOptionsTradeUpdated, this, dataCache); + return false; } - public intrinio.realtime.options.Quote getOptionsContractQuote(String contract){ - if (contracts.containsKey(contract)) - return contracts.get(contract).getQuote(); - else return null; + @Override + public intrinio.realtime.options.Quote getOptionsContractQuote(String contract) { + OptionsContractData optionsContractData = contracts.get(contract); + return optionsContractData != null ? optionsContractData.getLatestQuote() : null; } - public boolean setOptionsContractQuote(intrinio.realtime.options.Quote quote){ - //dirty set - if (contracts.containsKey(quote.contract())){ - return contracts.get(quote.contract()).setQuote(quote); - } - else{ - CurrentOptionsContractData data = new CurrentOptionsContractData(quote.contract(), null, quote, null); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(quote.contract(), data); - if (possiblyNewerData != null) - return possiblyNewerData.setQuote(quote); - return true; + @Override + public boolean setOptionsContractQuote(intrinio.realtime.options.Quote quote) { + if (quote != null) { + String contract = quote.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, quote, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setQuote(quote); } + return false; } - public boolean setOptionsQuote(intrinio.realtime.options.Quote quote, OnOptionsQuoteUpdated onOptionsQuoteUpdated, DataCache dataCache){ - CurrentOptionsContractData currentOptionsContractData; - String contract = quote.contract(); - if (contracts.containsKey(contract)) { - currentOptionsContractData = contracts.get(contract); - } - else { - CurrentOptionsContractData newData = new CurrentOptionsContractData(contract, null, quote, null); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(contract, newData); - currentOptionsContractData = possiblyNewerData == null ? newData : possiblyNewerData; + @Override + public boolean setOptionsContractQuote(intrinio.realtime.options.Quote quote, OnOptionsQuoteUpdated onOptionsQuoteUpdated, DataCache dataCache) { + if (quote != null) { + String contract = quote.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, quote, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setQuote(quote, onOptionsQuoteUpdated, this, dataCache); } - return currentOptionsContractData.setQuote(quote, onOptionsQuoteUpdated, this, dataCache); + return false; } - public intrinio.realtime.options.Refresh getOptionsContractRefresh(String contract){ - if (contracts.containsKey(contract)) - return contracts.get(contract).getRefresh(); - else return null; + @Override + public intrinio.realtime.options.Refresh getOptionsContractRefresh(String contract) { + OptionsContractData optionsContractData = contracts.get(contract); + return optionsContractData != null ? optionsContractData.getLatestRefresh() : null; } - public boolean setOptionsContractRefresh(intrinio.realtime.options.Refresh refresh){ - //dirty set - String contract = refresh.contract(); - if (contracts.containsKey(contract)){ - return contracts.get(contract).setRefresh(refresh); - } - else{ - CurrentOptionsContractData data = new CurrentOptionsContractData(contract, null, null, refresh); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(contract, data); - if (possiblyNewerData != null) - return possiblyNewerData.setRefresh(refresh); - return true; + @Override + public boolean setOptionsContractRefresh(intrinio.realtime.options.Refresh refresh) { + if (refresh != null) { + String contract = refresh.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, refresh, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setRefresh(refresh); } + return false; } - public boolean setOptionsRefresh(intrinio.realtime.options.Refresh refresh, OnOptionsRefreshUpdated onOptionsRefreshUpdated, DataCache dataCache){ - CurrentOptionsContractData currentOptionsContractData; - String contract = refresh.contract(); - if (contracts.containsKey(contract)) { - currentOptionsContractData = contracts.get(contract); + @Override + public boolean setOptionsContractRefresh(intrinio.realtime.options.Refresh refresh, OnOptionsRefreshUpdated onOptionsRefreshUpdated, DataCache dataCache) { + if (refresh != null) { + String contract = refresh.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, refresh, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setRefresh(refresh, onOptionsRefreshUpdated, this, dataCache); } - else { - CurrentOptionsContractData newData = new CurrentOptionsContractData(contract, null, null, refresh); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(contract, newData); - currentOptionsContractData = possiblyNewerData == null ? newData : possiblyNewerData; + return false; + } + + @Override + public intrinio.realtime.options.UnusualActivity getOptionsContractUnusualActivity(String contract) { + OptionsContractData optionsContractData = contracts.get(contract); + return optionsContractData != null ? optionsContractData.getLatestUnusualActivity() : null; + } + + @Override + public boolean setOptionsContractUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity) { + if (unusualActivity != null) { + String contract = unusualActivity.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, null, unusualActivity); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setUnusualActivity(unusualActivity); } - return currentOptionsContractData.setRefresh(refresh, onOptionsRefreshUpdated, this, dataCache); + return false; } - public Double getOptionsContractSupplementalDatum(String contract, String key){ - if (contracts.containsKey(contract)) - return contracts.get(contract).getSupplementaryDatum(key); - else return null; + @Override + public boolean setOptionsContractUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity, OnOptionsUnusualActivityUpdated onOptionsUnusualActivityUpdated, DataCache dataCache) { + if (unusualActivity != null) { + String contract = unusualActivity.contract(); + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, null, unusualActivity); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setUnusualActivity(unusualActivity, onOptionsUnusualActivityUpdated, this, dataCache); + } + return false; } - public boolean setOptionsContractSupplementalDatum(String contract, String key, double datum){ - CurrentOptionsContractData currentOptionsContractData; - if (contracts.containsKey(contract)) { - currentOptionsContractData = contracts.get(contract); + @Override + public Double getOptionsContractSupplementalDatum(String contract, String key) { + OptionsContractData optionsContractData = contracts.get(contract); + return optionsContractData != null ? optionsContractData.getSupplementaryDatum(key) : null; + } + + @Override + public boolean setOptionsContractSupplementalDatum(String contract, String key, Double datum, SupplementalDatumUpdate update) { + if (contract != null && !contract.trim().isEmpty()) { + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setSupplementaryDatum(key, datum, update); } - else { - CurrentOptionsContractData newData = new CurrentOptionsContractData(contract, null, null, null); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(contract, newData); - currentOptionsContractData = possiblyNewerData == null ? newData : possiblyNewerData; + return false; + } + + @Override + public boolean setOptionsContractSupplementalDatum(String contract, String key, Double datum, OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated, DataCache dataCache, SupplementalDatumUpdate update) { + if (contract != null && !contract.trim().isEmpty()) { + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setSupplementaryDatum(key, datum, onOptionsContractSupplementalDatumUpdated, this, dataCache, update); } - return currentOptionsContractData.setSupplementaryDatum(key, datum); + return false; } - boolean setOptionsContractSupplementalDatum(String contract, String key, double datum, OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated, DataCache dataCache){ - CurrentOptionsContractData currentOptionsContractData; - if (contracts.containsKey(contract)) { - currentOptionsContractData = contracts.get(contract); + @Override + public Greek getOptionsContractGreekData(String contract, String key) { + OptionsContractData optionsContractData = contracts.get(contract); + return optionsContractData != null ? optionsContractData.getGreekData(key) : null; + } + + @Override + public boolean setOptionsContractGreekData(String contract, String key, Greek data, GreekDataUpdate update) { + if (contract != null && !contract.trim().isEmpty()) { + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setGreekData(key, data, update); } - else { - CurrentOptionsContractData newData = new CurrentOptionsContractData(contract, null, null, null); - CurrentOptionsContractData possiblyNewerData = contracts.putIfAbsent(contract, newData); - currentOptionsContractData = possiblyNewerData == null ? newData : possiblyNewerData; + return false; + } + + @Override + public boolean setOptionsContractGreekData(String contract, String key, Greek data, OnOptionsContractGreekDataUpdated onOptionsContractGreekDataUpdated, DataCache dataCache, GreekDataUpdate update) { + if (contract != null && !contract.trim().isEmpty()) { + OptionsContractData currentOptionsContractData = contracts.get(contract); + if (currentOptionsContractData == null) { + OptionsContractData newDatum = new CurrentOptionsContractData(contract, null, null, null, null); + currentOptionsContractData = contracts.computeIfAbsent(contract, k -> newDatum); + } + return currentOptionsContractData.setGreekData(key, data, onOptionsContractGreekDataUpdated, this, dataCache, update); } - return currentOptionsContractData.setSupplementaryDatum(key, datum, onOptionsContractSupplementalDatumUpdated, this, dataCache); + return false; } - //region Private Methods private void Log(String message){ System.out.println(message); } - - //endregion Private Methods -} +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/DataCache.java b/src/intrinio/realtime/composite/DataCache.java index 8bfd1e4..37fccaf 100644 --- a/src/intrinio/realtime/composite/DataCache.java +++ b/src/intrinio/realtime/composite/DataCache.java @@ -1,62 +1,196 @@ package intrinio.realtime.composite; -import java.util.*; - -public interface DataCache -{ - Double getsupplementaryDatum(String key); - - boolean setsupplementaryDatum(String key, double datum); - +import java.util.Map; + +/** + * A non-transactional, thread-safe, volatile local cache for storing the latest data from a websocket. + */ +public interface DataCache { + + /** + * Get a supplementary data point from the general cache. + */ + Double getSupplementaryDatum(String key); + + /** + * Set a supplementary data point in the general cache. + */ + boolean setSupplementaryDatum(String key, Double datum, SupplementalDatumUpdate update); + + /** + * Get all supplementary data stored at the top level general cache. + */ Map getAllSupplementaryData(); - SecurityData getSecurityData(String tickerSymbol); - - Map getAllSecurityData(); - - intrinio.realtime.equities.Trade getEquityTrade(String tickerSymbol); - - boolean setEquityTrade(intrinio.realtime.equities.Trade trade); + /** + * Get a supplemental data point stored in a specific security's cache. + */ + Double getSecuritySupplementalDatum(String tickerSymbol, String key); - intrinio.realtime.equities.Quote getEquityQuote(String tickerSymbol); + /** + * Set a supplemental data point stored in a specific security's cache. + */ + boolean setSecuritySupplementalDatum(String tickerSymbol, String key, Double datum, SupplementalDatumUpdate update); - boolean setEquityQuote(intrinio.realtime.equities.Quote quote); + /** + * Get a supplemental data point stored in a specific option contract's cache. + */ + Double getOptionsContractSupplementalDatum(String tickerSymbol, String contract, String key); - OptionsContractData getOptionsContractData(String tickerSymbol, String contract); + /** + * Set a supplemental data point stored in a specific option contract's cache. + */ + boolean setOptionSupplementalDatum(String tickerSymbol, String contract, String key, Double datum, SupplementalDatumUpdate update); - intrinio.realtime.options.Trade getOptionsTrade(String tickerSymbol, String contract); + /** + * Get a supplemental data point stored in a specific option contract's cache. + */ + Greek getOptionsContractGreekData(String tickerSymbol, String contract, String key); - boolean setOptionsTrade(intrinio.realtime.options.Trade trade); + /** + * Set a supplemental data point stored in a specific option contract's cache. + */ + boolean setOptionGreekData(String tickerSymbol, String contract, String key, Greek data, GreekDataUpdate update); - intrinio.realtime.options.Quote getOptionsQuote(String tickerSymbol, String contract); + /** + * Get the cache for a specific security + */ + SecurityData getSecurityData(String tickerSymbol); - boolean setOptionsQuote(intrinio.realtime.options.Quote quote); + /** + * Get all security caches. + */ + Map getAllSecurityData(); - intrinio.realtime.options.Refresh getOptionsRefresh(String tickerSymbol, String contract); + /** + * Get a specific option contract's cache. + */ + OptionsContractData getOptionsContractData(String tickerSymbol, String contract); - boolean setOptionsRefresh(intrinio.realtime.options.Refresh refresh); + /** + * Get all option contract caches for a security. + */ + Map getAllOptionsContractData(String tickerSymbol); - Double getSecuritySupplementalDatum(String tickerSymbol, String key); + /** + * Get the latest trade for a security. + */ + intrinio.realtime.equities.Trade getLatestEquityTrade(String tickerSymbol); - boolean setSecuritySupplementalDatum(String tickerSymbol, String key, double datum); + /** + * Set the latest trade for a security. + */ + boolean setEquityTrade(intrinio.realtime.equities.Trade trade); - Double getOptionsContractSupplementalDatum(String tickerSymbol, String contract, String key); + /** + * Get the latest ask quote for a security. + */ + intrinio.realtime.equities.Quote getLatestEquityAskQuote(String tickerSymbol); - boolean setOptionSupplementalDatum(String tickerSymbol, String contract, String key, double datum); + /** + * Set the latest bid quote for a security. + */ + intrinio.realtime.equities.Quote getLatestEquityBidQuote(String tickerSymbol); - void setOnSupplementalDatumUpdated(OnSupplementalDatumUpdated onSupplementalDatumUpdated); + /** + * Set the latest quote for a security. + */ + boolean setEquityQuote(intrinio.realtime.equities.Quote quote); - void setOnSecuritySupplementalDatumUpdated(OnSecuritySupplementalDatumUpdated onSecuritySupplementalDatumUpdated); + /** + * Get the latest option contract trade. + */ + intrinio.realtime.options.Trade getLatestOptionsTrade(String tickerSymbol, String contract); - void setOnOptionSupplementalDatumUpdated(OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated); + /** + * Set the latest option contract trade. + */ + boolean setOptionsTrade(intrinio.realtime.options.Trade trade); - void setOnEquitiesQuoteUpdated(OnEquitiesQuoteUpdated onEquitiesQuoteUpdated); + /** + * Get the latest option contract quote. + */ + intrinio.realtime.options.Quote getLatestOptionsQuote(String tickerSymbol, String contract); - void setOnEquitiesTradeUpdated(OnEquitiesTradeUpdated onEquitiesTradeUpdated); + /** + * Set the latest option contract quote. + */ + boolean setOptionsQuote(intrinio.realtime.options.Quote quote); - void setOnOptionsQuoteUpdated(OnOptionsQuoteUpdated onOptionsQuoteUpdated); + /** + * Get the latest option contract refresh. + */ + intrinio.realtime.options.Refresh getLatestOptionsRefresh(String tickerSymbol, String contract); - void setOnOptionsTradeUpdated(OnOptionsTradeUpdated onOptionsTradeUpdated); + /** + * Set the latest option contract refresh. + */ + boolean setOptionsRefresh(intrinio.realtime.options.Refresh refresh); - void setOnOptionsRefreshUpdated(OnOptionsRefreshUpdated onOptionsRefreshUpdated); -} + /** + * Get the latest option contract unusual activity. + */ + intrinio.realtime.options.UnusualActivity getLatestOptionsUnusualActivity(String tickerSymbol, String contract); + + /** + * Set the latest option contract unusual activity. + */ + boolean setOptionsUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity); + + /** + * Set the callback when the top level supplemental data is updated. + */ + OnSupplementalDatumUpdated getSupplementalDatumUpdatedCallback(); + void setSupplementalDatumUpdatedCallback(OnSupplementalDatumUpdated callback); + + /** + * Set the callback when a security's supplemental data is updated. + */ + OnSecuritySupplementalDatumUpdated getSecuritySupplementalDatumUpdatedCallback(); + void setSecuritySupplementalDatumUpdatedCallback(OnSecuritySupplementalDatumUpdated callback); + + /** + * Set the callback when an option contract's supplemental data is updated. + */ + OnOptionsContractSupplementalDatumUpdated getOptionsContractSupplementalDatumUpdatedCallback(); + void setOptionsContractSupplementalDatumUpdatedCallback(OnOptionsContractSupplementalDatumUpdated callback); + + /** + * Set the callback for when the latest equity trade is updated. + */ + OnEquitiesTradeUpdated getEquitiesTradeUpdatedCallback(); + void setEquitiesTradeUpdatedCallback(OnEquitiesTradeUpdated callback); + + /** + * Set the callback for when the latest equity quote is updated. + */ + OnEquitiesQuoteUpdated getEquitiesQuoteUpdatedCallback(); + void setEquitiesQuoteUpdatedCallback(OnEquitiesQuoteUpdated callback); + + /** + * Set the callback for when the latest option trade is updated. + */ + OnOptionsTradeUpdated getOptionsTradeUpdatedCallback(); + void setOptionsTradeUpdatedCallback(OnOptionsTradeUpdated callback); + + /** + * Set the callback for when the latest option quote is updated. + */ + OnOptionsQuoteUpdated getOptionsQuoteUpdatedCallback(); + void setOptionsQuoteUpdatedCallback(OnOptionsQuoteUpdated callback); + + /** + * Set the callback for when the latest option refresh is updated. + */ + OnOptionsRefreshUpdated getOptionsRefreshUpdatedCallback(); + void setOptionsRefreshUpdatedCallback(OnOptionsRefreshUpdated callback); + + /** + * Set the callback for when the latest option unusual activity is updated. + */ + OnOptionsUnusualActivityUpdated getOptionsUnusualActivityUpdatedCallback(); + void setOptionsUnusualActivityUpdatedCallback(OnOptionsUnusualActivityUpdated callback); + + OnOptionsContractGreekDataUpdated getOptionsContractGreekDataUpdatedCallback(); + void setOptionsContractGreekDataUpdatedCallback(OnOptionsContractGreekDataUpdated callback); +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/DataCacheFactory.java b/src/intrinio/realtime/composite/DataCacheFactory.java new file mode 100644 index 0000000..39b9d51 --- /dev/null +++ b/src/intrinio/realtime/composite/DataCacheFactory.java @@ -0,0 +1,7 @@ +package intrinio.realtime.composite; + +public class DataCacheFactory { + public static DataCache create() { + return new CurrentDataCache(); + } +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/Greek.java b/src/intrinio/realtime/composite/Greek.java new file mode 100644 index 0000000..a7f3dc0 --- /dev/null +++ b/src/intrinio/realtime/composite/Greek.java @@ -0,0 +1,3 @@ +package intrinio.realtime.composite; + +public record Greek (double ImpliedVolatility, double Delta, double Gamma, double Theta, double Vega, boolean IsValid){} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/GreekDataUpdate.java b/src/intrinio/realtime/composite/GreekDataUpdate.java new file mode 100644 index 0000000..14c8b77 --- /dev/null +++ b/src/intrinio/realtime/composite/GreekDataUpdate.java @@ -0,0 +1,9 @@ +package intrinio.realtime.composite; + +/** + * The function used to update the Greek value in the cache. + */ +@FunctionalInterface +public interface GreekDataUpdate { + Greek greekDataUpdate(String key, Greek oldValue, Greek newValue); +} diff --git a/src/intrinio/realtime/composite/GreekUpdateFrequency.java b/src/intrinio/realtime/composite/GreekUpdateFrequency.java new file mode 100644 index 0000000..fd55075 --- /dev/null +++ b/src/intrinio/realtime/composite/GreekUpdateFrequency.java @@ -0,0 +1,40 @@ +package intrinio.realtime.composite; + +import java.util.EnumSet; + +public enum GreekUpdateFrequency { + EVERY_OPTIONS_TRADE_UPDATE(1), + EVERY_OPTIONS_QUOTE_UPDATE(2), + EVERY_RISK_FREE_INTEREST_RATE_UPDATE(4), + EVERY_DIVIDEND_YIELD_UPDATE(8), + EVERY_EQUITY_TRADE_UPDATE(16), + EVERY_EQUITY_QUOTE_UPDATE(32); + + private final int value; + + GreekUpdateFrequency(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static int combine(EnumSet set) { + int combined = 0; + for (GreekUpdateFrequency freq : set) { + combined |= freq.getValue(); + } + return combined; + } + + public static EnumSet fromValue(int value) { + EnumSet set = EnumSet.noneOf(GreekUpdateFrequency.class); + for (GreekUpdateFrequency freq : values()) { + if ((value & freq.getValue()) != 0) { + set.add(freq); + } + } + return set; + } +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/OnEquitiesQuoteUpdated.java b/src/intrinio/realtime/composite/OnEquitiesQuoteUpdated.java index be1676f..8f3e83c 100644 --- a/src/intrinio/realtime/composite/OnEquitiesQuoteUpdated.java +++ b/src/intrinio/realtime/composite/OnEquitiesQuoteUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnEquitiesQuoteUpdated { - void onEquitiesQuoteUpdated(SecurityData SecurityData, DataCache DataCache); + void onEquitiesQuoteUpdated(SecurityData securityData, DataCache dataCache, intrinio.realtime.equities.Quote quote); } diff --git a/src/intrinio/realtime/composite/OnEquitiesTradeUpdated.java b/src/intrinio/realtime/composite/OnEquitiesTradeUpdated.java index d595bfe..8b3ac13 100644 --- a/src/intrinio/realtime/composite/OnEquitiesTradeUpdated.java +++ b/src/intrinio/realtime/composite/OnEquitiesTradeUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnEquitiesTradeUpdated { - void onEquitiesTradeUpdated(SecurityData securityData, DataCache dataCache); + void onEquitiesTradeUpdated(SecurityData securityData, DataCache dataCache, intrinio.realtime.equities.Trade trade); } diff --git a/src/intrinio/realtime/composite/OnOptionsContractGreekDataUpdated.java b/src/intrinio/realtime/composite/OnOptionsContractGreekDataUpdated.java new file mode 100644 index 0000000..1090bb1 --- /dev/null +++ b/src/intrinio/realtime/composite/OnOptionsContractGreekDataUpdated.java @@ -0,0 +1,6 @@ +package intrinio.realtime.composite; + +@FunctionalInterface +public interface OnOptionsContractGreekDataUpdated { + void onOptionsContractGreekDataUpdated(String key, Greek datum, OptionsContractData optionsContractData, SecurityData securityData, DataCache dataCache); +} diff --git a/src/intrinio/realtime/composite/OnOptionsContractSupplementalDatumUpdated.java b/src/intrinio/realtime/composite/OnOptionsContractSupplementalDatumUpdated.java index f750f5d..706c890 100644 --- a/src/intrinio/realtime/composite/OnOptionsContractSupplementalDatumUpdated.java +++ b/src/intrinio/realtime/composite/OnOptionsContractSupplementalDatumUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnOptionsContractSupplementalDatumUpdated { - void onOptionsContractSupplementalDatumUpdated(String key, double datum, OptionsContractData optionsContractData, SecurityData securityData, DataCache dataCache); + void onOptionsContractSupplementalDatumUpdated(String key, Double datum, OptionsContractData optionsContractData, SecurityData securityData, DataCache dataCache); } diff --git a/src/intrinio/realtime/composite/OnOptionsQuoteUpdated.java b/src/intrinio/realtime/composite/OnOptionsQuoteUpdated.java index 3e9f192..9879778 100644 --- a/src/intrinio/realtime/composite/OnOptionsQuoteUpdated.java +++ b/src/intrinio/realtime/composite/OnOptionsQuoteUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnOptionsQuoteUpdated { - void onOptionsQuoteUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData); + void onOptionsQuoteUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData, intrinio.realtime.options.Quote quote); } diff --git a/src/intrinio/realtime/composite/OnOptionsRefreshUpdated.java b/src/intrinio/realtime/composite/OnOptionsRefreshUpdated.java index f51a36c..59ea966 100644 --- a/src/intrinio/realtime/composite/OnOptionsRefreshUpdated.java +++ b/src/intrinio/realtime/composite/OnOptionsRefreshUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnOptionsRefreshUpdated { - void onOptionsRefreshUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData); + void onOptionsRefreshUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData, intrinio.realtime.options.Refresh refresh); } diff --git a/src/intrinio/realtime/composite/OnOptionsTradeUpdated.java b/src/intrinio/realtime/composite/OnOptionsTradeUpdated.java index ebca6f4..1d80a89 100644 --- a/src/intrinio/realtime/composite/OnOptionsTradeUpdated.java +++ b/src/intrinio/realtime/composite/OnOptionsTradeUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnOptionsTradeUpdated { - void onOptionsTradeUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData); + void onOptionsTradeUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData, intrinio.realtime.options.Trade trade); } diff --git a/src/intrinio/realtime/composite/OnOptionsUnusualActivityUpdated.java b/src/intrinio/realtime/composite/OnOptionsUnusualActivityUpdated.java new file mode 100644 index 0000000..1ee3f60 --- /dev/null +++ b/src/intrinio/realtime/composite/OnOptionsUnusualActivityUpdated.java @@ -0,0 +1,6 @@ +package intrinio.realtime.composite; + +@FunctionalInterface +public interface OnOptionsUnusualActivityUpdated { + void onOptionsUnusualActivityUpdated(OptionsContractData optionsContractData, DataCache dataCache, SecurityData securityData, intrinio.realtime.options.UnusualActivity unusualActivity); +} diff --git a/src/intrinio/realtime/composite/OnSecuritySupplementalDatumUpdated.java b/src/intrinio/realtime/composite/OnSecuritySupplementalDatumUpdated.java index 1101811..b7b569b 100644 --- a/src/intrinio/realtime/composite/OnSecuritySupplementalDatumUpdated.java +++ b/src/intrinio/realtime/composite/OnSecuritySupplementalDatumUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnSecuritySupplementalDatumUpdated { - void onSecuritySupplementalDatumUpdated(String key, double datum, SecurityData securityData, DataCache dataCache); + void onSecuritySupplementalDatumUpdated(String key, Double datum, SecurityData securityData, DataCache dataCache); } diff --git a/src/intrinio/realtime/composite/OnSupplementalDatumUpdated.java b/src/intrinio/realtime/composite/OnSupplementalDatumUpdated.java index d0c0bc0..e956371 100644 --- a/src/intrinio/realtime/composite/OnSupplementalDatumUpdated.java +++ b/src/intrinio/realtime/composite/OnSupplementalDatumUpdated.java @@ -1,5 +1,6 @@ package intrinio.realtime.composite; +@FunctionalInterface public interface OnSupplementalDatumUpdated { - void onSupplementalDatumUpdated(String key, double datum, DataCache dataCache); + void onSupplementalDatumUpdated(String key, Double datum, DataCache dataCache); } diff --git a/src/intrinio/realtime/composite/OptionsContractData.java b/src/intrinio/realtime/composite/OptionsContractData.java index 10009f5..628260e 100644 --- a/src/intrinio/realtime/composite/OptionsContractData.java +++ b/src/intrinio/realtime/composite/OptionsContractData.java @@ -1,26 +1,39 @@ package intrinio.realtime.composite; +import intrinio.realtime.options.Trade; +import intrinio.realtime.options.Quote; +import intrinio.realtime.options.Refresh; +import intrinio.realtime.options.UnusualActivity; +import intrinio.realtime.options.QuoteType; import java.util.Map; +/** + * Not for Use yet. Subject to change. + */ public interface OptionsContractData { - String getContract(); - intrinio.realtime.options.Trade getTrade(); - - intrinio.realtime.options.Quote getQuote(); - - intrinio.realtime.options.Refresh getRefresh(); - - boolean setTrade(intrinio.realtime.options.Trade trade); + Trade getLatestTrade(); + Quote getLatestQuote(); + Refresh getLatestRefresh(); + UnusualActivity getLatestUnusualActivity(); - boolean setQuote(intrinio.realtime.options.Quote quote); - - boolean setRefresh(intrinio.realtime.options.Refresh refresh); + boolean setTrade(Trade trade); + boolean setTrade(Trade trade, OnOptionsTradeUpdated onOptionsTradeUpdated, SecurityData securityData, DataCache dataCache); + boolean setQuote(Quote quote); + boolean setQuote(Quote quote, OnOptionsQuoteUpdated onOptionsQuoteUpdated, SecurityData securityData, DataCache dataCache); + boolean setRefresh(Refresh refresh); + boolean setRefresh(Refresh refresh, OnOptionsRefreshUpdated onOptionsRefreshUpdated, SecurityData securityData, DataCache dataCache); + boolean setUnusualActivity(UnusualActivity unusualActivity); + boolean setUnusualActivity(UnusualActivity unusualActivity, OnOptionsUnusualActivityUpdated onOptionsUnusualActivityUpdated, SecurityData securityData, DataCache dataCache); Double getSupplementaryDatum(String key); - - boolean setSupplementaryDatum(String key, double datum); - + boolean setSupplementaryDatum(String key, Double datum, SupplementalDatumUpdate update); + boolean setSupplementaryDatum(String key, Double datum, OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated, SecurityData securityData, DataCache dataCache, SupplementalDatumUpdate update); Map getAllSupplementaryData(); -} + + Greek getGreekData(String key); + boolean setGreekData(String key, Greek datum, GreekDataUpdate update); + boolean setGreekData(String key, Greek datum, OnOptionsContractGreekDataUpdated onOptionsContractGreekDataUpdated, SecurityData securityData, DataCache dataCache, GreekDataUpdate update); + Map getAllGreekData(); +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/SecurityData.java b/src/intrinio/realtime/composite/SecurityData.java index 3490f44..a25e83b 100644 --- a/src/intrinio/realtime/composite/SecurityData.java +++ b/src/intrinio/realtime/composite/SecurityData.java @@ -1,45 +1,64 @@ package intrinio.realtime.composite; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public interface SecurityData { - String getTickerSymbol(); + intrinio.realtime.equities.Trade getLatestEquitiesTrade(); + intrinio.realtime.equities.Quote getLatestEquitiesAskQuote(); + intrinio.realtime.equities.Quote getLatestEquitiesBidQuote(); + Double getSupplementaryDatum(String key); - boolean setSupplementaryDatum(String key, double datum); + boolean setSupplementaryDatum(String key, Double datum, SupplementalDatumUpdate update); + boolean setSupplementaryDatum(String key, Double datum, OnSecuritySupplementalDatumUpdated onSecuritySupplementalDatumUpdated, DataCache dataCache, SupplementalDatumUpdate update); Map getAllSupplementaryData(); - intrinio.realtime.equities.Trade getEquitiesTrade(); + boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade); + boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade, OnEquitiesTradeUpdated onEquitiesTradeUpdated, DataCache dataCache); - intrinio.realtime.equities.Quote getEquitiesQuote(); + boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote); + boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote, OnEquitiesQuoteUpdated onEquitiesQuoteUpdated, DataCache dataCache); OptionsContractData getOptionsContractData(String contract); Map getAllOptionsContractData(); - List getContractNames(String ticker); - - boolean setEquitiesTrade(intrinio.realtime.equities.Trade trade); - - boolean setEquitiesQuote(intrinio.realtime.equities.Quote quote); + List getContractNames(); intrinio.realtime.options.Trade getOptionsContractTrade(String contract); boolean setOptionsContractTrade(intrinio.realtime.options.Trade trade); + boolean setOptionsContractTrade(intrinio.realtime.options.Trade trade, OnOptionsTradeUpdated onOptionsTradeUpdated, DataCache dataCache); intrinio.realtime.options.Quote getOptionsContractQuote(String contract); boolean setOptionsContractQuote(intrinio.realtime.options.Quote quote); + boolean setOptionsContractQuote(intrinio.realtime.options.Quote quote, OnOptionsQuoteUpdated onOptionsQuoteUpdated, DataCache dataCache); intrinio.realtime.options.Refresh getOptionsContractRefresh(String contract); boolean setOptionsContractRefresh(intrinio.realtime.options.Refresh refresh); + boolean setOptionsContractRefresh(intrinio.realtime.options.Refresh refresh, OnOptionsRefreshUpdated onOptionsRefreshUpdated, DataCache dataCache); + + intrinio.realtime.options.UnusualActivity getOptionsContractUnusualActivity(String contract); + + boolean setOptionsContractUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity); + boolean setOptionsContractUnusualActivity(intrinio.realtime.options.UnusualActivity unusualActivity, OnOptionsUnusualActivityUpdated onOptionsUnusualActivityUpdated, DataCache dataCache); Double getOptionsContractSupplementalDatum(String contract, String key); - boolean setOptionsContractSupplementalDatum(String contract, String key, double datum); -} + boolean setOptionsContractSupplementalDatum(String contract, String key, Double datum, SupplementalDatumUpdate update); + boolean setOptionsContractSupplementalDatum(String contract, String key, Double datum, OnOptionsContractSupplementalDatumUpdated onOptionsContractSupplementalDatumUpdated, DataCache dataCache, SupplementalDatumUpdate update); + + Greek getOptionsContractGreekData(String contract, String key); + + boolean setOptionsContractGreekData(String contract, String key, Greek data, GreekDataUpdate update); + boolean setOptionsContractGreekData(String contract, String key, Greek data, OnOptionsContractGreekDataUpdated onOptionsContractGreekDataUpdated, DataCache dataCache, GreekDataUpdate update); +} \ No newline at end of file diff --git a/src/intrinio/realtime/composite/SupplementalDatumUpdate.java b/src/intrinio/realtime/composite/SupplementalDatumUpdate.java new file mode 100644 index 0000000..e17647a --- /dev/null +++ b/src/intrinio/realtime/composite/SupplementalDatumUpdate.java @@ -0,0 +1,9 @@ +package intrinio.realtime.composite; + +/** + * The function used to update the Supplemental value in the cache. + */ +@FunctionalInterface +public interface SupplementalDatumUpdate { + Double supplementalDatumUpdate(String key, Double oldValue, Double newValue); +} diff --git a/src/intrinio/realtime/equities/Client.java b/src/intrinio/realtime/equities/Client.java index 4f76276..127152b 100644 --- a/src/intrinio/realtime/equities/Client.java +++ b/src/intrinio/realtime/equities/Client.java @@ -52,7 +52,7 @@ public class Client implements WebSocket.Listener { private Thread[] processDataThreads; private boolean isCancellationRequested = false; private String HeaderClientInformationKey = "Client-Information"; - private String HeaderClientInformationValue = "IntrinioRealtimeJavaSDKv7.2"; + private String HeaderClientInformationValue = "IntrinioRealtimeJavaSDKv8.0"; private String HeaderMessageVersionKey = "UseNewEquitiesFormat"; private String HeaderMessageVersionValue = "v2"; //endregion Data Members @@ -127,6 +127,8 @@ private String getAuthUrl() throws Exception { break; case CBOE_ONE: authUrl = "https://cboe-one.intrinio.com/auth?api_key=" + config.getEquitiesApiKey(); break; + case EQUITIES_EDGE: authUrl = "https://equities-edge.intrinio.com/auth?api_key=" + config.getEquitiesApiKey(); + break; case MANUAL: authUrl = "http://" + config.getEquitiesIpAddress() + "/auth?api_key=" + config.getEquitiesApiKey(); break; default: throw new Exception("Provider not specified!"); @@ -148,6 +150,8 @@ private String getWebSocketUrl (String token) throws Exception { break; case CBOE_ONE: wsUrl = "wss://cboe-one.intrinio.com/socket/websocket?vsn=1.0.0&token=" + token + delayedPart; break; + case EQUITIES_EDGE: wsUrl = "wss://equities-edge.intrinio.com/socket/websocket?vsn=1.0.0&token=" + token + delayedPart; + break; case MANUAL: wsUrl = "ws://" + config.getEquitiesIpAddress() + "/socket/websocket?vsn=1.0.0&token=" + token + delayedPart; break; default: throw new Exception("Provider not specified!"); diff --git a/src/intrinio/realtime/equities/Provider.java b/src/intrinio/realtime/equities/Provider.java index a9a14d7..8905b20 100644 --- a/src/intrinio/realtime/equities/Provider.java +++ b/src/intrinio/realtime/equities/Provider.java @@ -7,5 +7,6 @@ public enum Provider { DELAYED_SIP, NASDAQ_BASIC, CBOE_ONE, - IEX + IEX, + EQUITIES_EDGE } diff --git a/src/intrinio/realtime/equities/SubProvider.java b/src/intrinio/realtime/equities/SubProvider.java index 8f5253e..34235b8 100644 --- a/src/intrinio/realtime/equities/SubProvider.java +++ b/src/intrinio/realtime/equities/SubProvider.java @@ -8,5 +8,6 @@ public enum SubProvider { OTC, NASDAQ_BASIC, IEX, - CBOE_ONE + CBOE_ONE, + EQUITIES_EDGE } diff --git a/src/intrinio/realtime/options/Client.java b/src/intrinio/realtime/options/Client.java index 1db7980..027b208 100644 --- a/src/intrinio/realtime/options/Client.java +++ b/src/intrinio/realtime/options/Client.java @@ -35,7 +35,7 @@ public class Client implements WebSocket.Listener { private final Lock dataBucketLock = new ReentrantLock(); private final LinkedBlockingDeque> dataBucket = new LinkedBlockingDeque>(); private final WebSocketState wsState = new WebSocketState(); - private final String Version = "IntrinioRealtimeOptionsJavaSDKv7.2"; + private final String Version = "IntrinioRealtimeOptionsJavaSDKv8.0"; //endregion Final data members //region Data Members @@ -124,6 +124,8 @@ private String getAuthUrl() throws Exception { switch (config.getOptionsProvider()) { case OPRA: authUrl = "https://realtime-options.intrinio.com/auth?api_key=" + config.getOptionsApiKey(); break; + case OPTIONS_EDGE: authUrl = "https://options-edge.intrinio.com/auth?api_key=" + config.getOptionsApiKey(); + break; case MANUAL: authUrl = "http://" + config.getOptionsIpAddress() + "/auth?api_key=" + config.getOptionsApiKey(); break; default: throw new Exception("Provider not specified!"); @@ -136,6 +138,8 @@ private String getWebSocketUrl (String token) throws Exception { switch (config.getOptionsProvider()) { case OPRA: wsUrl = "wss://realtime-options.intrinio.com/socket/websocket?vsn=1.0.0&token=" + token + (this.config.isDelayed() ? "&delayed=true" : ""); break; + case OPTIONS_EDGE: wsUrl = "wss://options-edge.intrinio.com/socket/websocket?vsn=1.0.0&token=" + token + (this.config.isDelayed() ? "&delayed=true" : ""); + break; case MANUAL: wsUrl = "ws://" + config.getOptionsIpAddress() + "/socket/websocket?vsn=1.0.0&token=" + token + (this.config.isDelayed() ? "&delayed=true" : ""); break; default: throw new Exception("Provider not specified!"); diff --git a/src/intrinio/realtime/options/Provider.java b/src/intrinio/realtime/options/Provider.java index 72b255f..beb8343 100644 --- a/src/intrinio/realtime/options/Provider.java +++ b/src/intrinio/realtime/options/Provider.java @@ -3,5 +3,6 @@ public enum Provider { NONE, OPRA, + OPTIONS_EDGE, MANUAL } diff --git a/src/intrinio/realtime/options/QuoteType.java b/src/intrinio/realtime/options/QuoteType.java new file mode 100644 index 0000000..1d38645 --- /dev/null +++ b/src/intrinio/realtime/options/QuoteType.java @@ -0,0 +1,7 @@ +package intrinio.realtime.options; + +public enum QuoteType +{ + Ask, + Bid +}