diff --git a/components/bitbucket_data_center/bitbucket_data_center.app.mjs b/components/bitbucket_data_center/bitbucket_data_center.app.mjs index 922ead122293b..a193358cf0c4a 100644 --- a/components/bitbucket_data_center/bitbucket_data_center.app.mjs +++ b/components/bitbucket_data_center/bitbucket_data_center.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/brillium/brillium.app.mjs b/components/brillium/brillium.app.mjs index d324fcb1d31d4..318c547227b06 100644 --- a/components/brillium/brillium.app.mjs +++ b/components/brillium/brillium.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/frontify/frontify.app.mjs b/components/frontify/frontify.app.mjs index 35b602820c598..f7902b68e5e75 100644 --- a/components/frontify/frontify.app.mjs +++ b/components/frontify/frontify.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/membership_io/membership_io.app.mjs b/components/membership_io/membership_io.app.mjs index 16dad612f6a0e..0ff214f8ca984 100644 --- a/components/membership_io/membership_io.app.mjs +++ b/components/membership_io/membership_io.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/polygon/actions/get-company-financials/get-company-financials.mjs b/components/polygon/actions/get-company-financials/get-company-financials.mjs new file mode 100644 index 0000000000000..8db7b40ad0e2c --- /dev/null +++ b/components/polygon/actions/get-company-financials/get-company-financials.mjs @@ -0,0 +1,80 @@ +import { + SORT_HISTORICAL_OPTIONS, + SORT_OPTIONS, TIMEFRAME_OPTIONS, +} from "../../common/constants.mjs"; +import polygon from "../../polygon.app.mjs"; + +export default { + key: "polygon-get-company-financials", + name: "Get Company Financials", + description: "Retrieves financial details for a specific company by stock ticker. [See the documentation](https://polygon.io/docs/stocks/get_vx_reference_financials).", + version: "0.0.1", + type: "action", + props: { + polygon, + stockTicker: { + propDefinition: [ + polygon, + "stockTicker", + ], + }, + filingDate: { + type: "string", + label: "Filing Date", + description: "Query by the date when the filing with financials data was filed in **YYYY-MM-DD** format.", + optional: true, + }, + periodOfReportDate: { + type: "string", + label: "Period Of Report Date", + description: "The period of report for the filing with financials data in **YYYY-MM-DD** format.", + optional: true, + }, + timeframe: { + type: "string", + label: "Timeframe", + description: "Query by timeframe. Annual financials originate from 10-K filings, and quarterly financials originate from 10-Q filings. Note: Most companies do not file quarterly reports for Q4 and instead include those financials in their annual report, so some companies my not return quarterly financials for Q4", + options: TIMEFRAME_OPTIONS, + optional: true, + }, + order: { + type: "string", + label: "Order", + description: "Order results based on the `Sort` field.", + options: SORT_OPTIONS, + optional: true, + }, + limit: { + type: "integer", + label: "Limit", + description: "Limit the number of results returned.", + optional: true, + min: 1, + max: 100, + default: 10, + }, + sort: { + type: "string", + label: "Sort", + description: "Sort field used for ordering", + options: SORT_HISTORICAL_OPTIONS, + optional: true, + }, + }, + async run({ $ }) { + const financialDetails = await this.polygon.getFinancialDetails({ + $, + params: { + ticker: this.stockTicker, + filing_date: this.filingDate, + period_of_report_date: this.periodOfReportDate, + timeframe: this.timeframe, + order: this.order, + limit: this.limit, + sort: this.sort, + }, + }); + $.export("$summary", `Successfully retrieved financial details for ${this.stockTicker}`); + return financialDetails; + }, +}; diff --git a/components/polygon/actions/get-historical-prices/get-historical-prices.mjs b/components/polygon/actions/get-historical-prices/get-historical-prices.mjs new file mode 100644 index 0000000000000..a65c1d7cc79cf --- /dev/null +++ b/components/polygon/actions/get-historical-prices/get-historical-prices.mjs @@ -0,0 +1,84 @@ +import { + SORT_OPTIONS, + TIMESPAN_OPTIONS, +} from "../../common/constants.mjs"; +import polygon from "../../polygon.app.mjs"; + +export default { + key: "polygon-get-historical-prices", + name: "Get Historical Prices", + description: "Fetches historical price data for a specified stock ticker within a date range. [See the documentation](https://polygon.io/docs/stocks/get_v2_aggs_ticker__stocksticker__range__multiplier___timespan___from___to)", + version: "0.0.1", + type: "action", + props: { + polygon, + stockTicker: { + propDefinition: [ + polygon, + "stockTicker", + ], + }, + multiplier: { + type: "integer", + label: "Multiplier", + description: "The size of the timespan multiplier.", + default: 1, + }, + timespan: { + type: "string", + label: "Timespan", + description: "The size of the time window.", + options: TIMESPAN_OPTIONS, + }, + from: { + type: "string", + label: "From Date", + description: "The start of the aggregate time window. Either a date with the format **YYYY-MM-DD** or a millisecond timestamp.", + }, + to: { + type: "string", + label: "To Date", + description: "The end of the aggregate time window. Either a date with the format **YYYY-MM-DD** or a millisecond timestamp.", + }, + adjusted: { + propDefinition: [ + polygon, + "adjusted", + ], + optional: true, + }, + sort: { + type: "string", + label: "Sort", + description: "Sort the results by timestamp. asc will return results in ascending order (oldest at the top), desc will return results in descending order (newest at the top).", + options: SORT_OPTIONS, + optional: true, + }, + limit: { + type: "integer", + label: "Limit", + description: "Limits the number of base aggregates queried to create the aggregate results. Max 50000 and Default 5000. Read more about how limit is used to calculate aggregate results in our article on [Aggregate Data API Improvements](https://polygon.io/blog/aggs-api-updates).", + optional: true, + min: 1, + max: 50000, + default: 5000, + }, + }, + async run({ $ }) { + const response = await this.polygon.getHistoricalPriceData({ + $, + stockTicker: this.stockTicker, + multiplier: this.multiplier, + timespan: this.timespan, + from: this.from, + to: this.to, + params: { + adjusted: this.adjusted, + sort: this.sort, + limit: this.limit, + }, + }); + $.export("$summary", `Fetched historical prices for ${this.stockTicker} from ${this.from} to ${this.to}.`); + return response; + }, +}; diff --git a/components/polygon/actions/get-stock-price/get-stock-price.mjs b/components/polygon/actions/get-stock-price/get-stock-price.mjs new file mode 100644 index 0000000000000..f4c92f5df8439 --- /dev/null +++ b/components/polygon/actions/get-stock-price/get-stock-price.mjs @@ -0,0 +1,51 @@ +import polygon from "../../polygon.app.mjs"; + +export default { + key: "polygon-get-stock-price", + name: "Get Stock Price", + description: "Get the open, close and afterhours prices of a stock symbol on a certain date. [See the documentation](https://polygon.io/docs/stocks/get_v1_open-close__stocksticker___date)", + version: "0.0.1", + type: "action", + props: { + polygon, + stockTicker: { + propDefinition: [ + polygon, + "stockTicker", + ], + }, + date: { + type: "string", + label: "Date", + description: "The date of the requested open/close in the format YYYY-MM-DD.", + }, + adjusted: { + propDefinition: [ + polygon, + "adjusted", + ], + optional: true, + }, + }, + async run({ $ }) { + try { + + const response = await this.polygon.getCurrentPrice({ + $, + date: this.date, + stockTicker: this.stockTicker, + params: { + adjusted: this.adjusted, + }, + }); + + $.export("$summary", `Successfully fetched the price of ${this.stockTicker}`); + return response; + } catch ({ response }) { + if (response.status === 404) { + $.export("$summary", `No data found for ${this.stockTicker}`); + return {}; + } + } + }, +}; diff --git a/components/polygon/common/constants.mjs b/components/polygon/common/constants.mjs new file mode 100644 index 0000000000000..552ef3b23e526 --- /dev/null +++ b/components/polygon/common/constants.mjs @@ -0,0 +1,26 @@ +export const TIMESPAN_OPTIONS = [ + "second", + "minute", + "hour", + "day", + "week", + "month", + "quarter", + "year", +]; + +export const TIMEFRAME_OPTIONS = [ + "annual", + "quarterly", + "ttm", +]; + +export const SORT_OPTIONS = [ + "asc", + "desc", +]; + +export const SORT_HISTORICAL_OPTIONS = [ + "filing_date", + "period_of_report_date", +]; diff --git a/components/polygon/common/utils.mjs b/components/polygon/common/utils.mjs new file mode 100644 index 0000000000000..3354dda7c3efc --- /dev/null +++ b/components/polygon/common/utils.mjs @@ -0,0 +1,7 @@ +export const parseNextPage = (nextUrl = null) => { + if (nextUrl) { + const url = new URL(nextUrl); + nextUrl = url.searchParams.get("cursor"); + } + return nextUrl; +}; diff --git a/components/polygon/package.json b/components/polygon/package.json index b788cc6585d28..249c9ac98ce11 100644 --- a/components/polygon/package.json +++ b/components/polygon/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/polygon", - "version": "0.6.0", + "version": "0.7.0", "description": "Pipedream polygon Components", "main": "polygon.app.mjs", "keywords": [ @@ -13,6 +13,6 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^3.0.0" + "@pipedream/platform": "^3.0.3" } } diff --git a/components/polygon/polygon.app.mjs b/components/polygon/polygon.app.mjs index 4ff0d5c269030..419e4852e9cf9 100644 --- a/components/polygon/polygon.app.mjs +++ b/components/polygon/polygon.app.mjs @@ -1,11 +1,143 @@ +import { axios } from "@pipedream/platform"; +import { parseNextPage } from "./common/utils.mjs"; + export default { type: "app", app: "polygon", - propDefinitions: {}, + propDefinitions: { + stockTicker: { + type: "string", + label: "Stock Ticker", + description: "Specify a case-sensitive ticker symbol. For example, AAPL represents Apple Inc.", + async options({ + page, prevContext, + }) { + const parsedPage = parseNextPage(prevContext.nextPage); + + const { + results, next_url: next, + } = await this.listStockTickers({ + params: { + page, + cursor: parsedPage, + }, + }); + + return { + options: results.map(({ + ticker: value, name, + }) => ({ + label: `${name} (${value})`, + value, + })), + context: { + nextPage: next, + }, + }; + }, + }, + adjusted: { + type: "boolean", + label: "Adjusted", + description: "Whether or not the results are adjusted for splits. By default, results are adjusted. Set this to false to get results that are NOT adjusted for splits.", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.polygon.io"; + }, + _headers() { + return { + Authorization: `Bearer ${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + getCurrentPrice({ + stockTicker, date, ...opts + }) { + return this._makeRequest({ + path: `/v1/open-close/${stockTicker}/${date}`, + ...opts, + }); + }, + getHistoricalPriceData({ + stockTicker, multiplier, timespan, from, to, ...opts + }) { + return this._makeRequest({ + path: `/v2/aggs/ticker/${stockTicker}/range/${multiplier}/${timespan}/${from}/${to}`, + ...opts, + }); + }, + getPreviousClose({ + stockTicker, ...opts + }) { + return this._makeRequest({ + path: `/v2/aggs/ticker/${stockTicker}/prev`, + ...opts, + }); + }, + listStockTickers(opts = {}) { + return this._makeRequest({ + path: "/v3/reference/tickers", + ...opts, + }); + }, + getFinancialDetails(opts = {}) { + return this._makeRequest({ + path: "/vX/reference/financials", + ...opts, + }); + }, + getNewsArticles(opts = {}) { + return this._makeRequest({ + path: "/v2/reference/news", + ...opts, + }); + }, + getTradeEvents({ + stockTicker, ...opts + }) { + return this._makeRequest({ + path: `/v3/trades/${stockTicker}`, + ...opts, + }); + }, + async *paginate({ + fn, params = {}, parseDataFn, maxResults = null, ...opts + }) { + let next; + let count = 0; + + do { + params.cursor = next; + const data = await fn({ + params, + ...opts, + }); + + const { + parsedData, nextPage, + } = parseDataFn(data); + + for (const d of parsedData) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + next = nextPage; + + } while (next); }, }, }; diff --git a/components/polygon/sources/common/base.mjs b/components/polygon/sources/common/base.mjs new file mode 100644 index 0000000000000..fee7bc2df3d7e --- /dev/null +++ b/components/polygon/sources/common/base.mjs @@ -0,0 +1,66 @@ +import { + ConfigurationError, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import app from "../../polygon.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async emitEvent(maxResults = false) { + try { + const lastDate = this._getLastDate(); + + const response = this.app.paginate({ + fn: this.getFunction(), + parseDataFn: this.parseData, + ...this.getData(lastDate), + maxResults, + }); + + let responseArray = []; + for await (const item of response) { + if (Date.parse(item.date) <= Date.parse(lastDate)) break; + responseArray.push(item); + } + + if (responseArray.length) { + this._setLastDate(responseArray[0].published_utc); + } + + for (const item of responseArray.reverse()) { + const ts = Date.parse(item.published_utc || item.sip_timestamp || new Date()); + this.$emit(item, { + id: item.id || ts, + summary: this.getSummary(item), + ts, + }); + } + } catch ({ response }) { + throw new ConfigurationError(response.data.message); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/polygon/sources/new-market-news/new-market-news.mjs b/components/polygon/sources/new-market-news/new-market-news.mjs new file mode 100644 index 0000000000000..5516761bc3532 --- /dev/null +++ b/components/polygon/sources/new-market-news/new-market-news.mjs @@ -0,0 +1,43 @@ +import { parseNextPage } from "../../common/utils.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "polygon-new-market-news", + name: "New Market News", + description: "Emit new events when a news article related to the stock market is published.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.app.getNewsArticles; + }, + getSummary(item) { + return `New Market News: ${item.title}`; + }, + getData(lastDate) { + return { + params: { + "limit": 1000, + "sort": "published_utc", + "order": "desc", + "published_utc.gt": lastDate, + }, + }; + }, + parseData({ + results, next_url: next, + }) { + const parsedPage = parseNextPage(next); + + return { + parsedData: results, + nextPage: parsedPage, + }; + }, + }, + sampleEmit, +}; diff --git a/components/polygon/sources/new-market-news/test-event.mjs b/components/polygon/sources/new-market-news/test-event.mjs new file mode 100644 index 0000000000000..dc74e73e01e3e --- /dev/null +++ b/components/polygon/sources/new-market-news/test-event.mjs @@ -0,0 +1,31 @@ +export default { + "amp_url": "https://m.uk.investing.com/news/stock-market-news/markets-are-underestimating-fed-cuts-ubs-3559968?ampMode=1", + "article_url": "https://uk.investing.com/news/stock-market-news/markets-are-underestimating-fed-cuts-ubs-3559968", + "author": "Sam Boughedda", + "description": "UBS analysts warn that markets are underestimating the extent of future interest rate cuts by the Federal Reserve, as the weakening economy is likely to justify more cuts than currently anticipated.", + "id": "8ec638777ca03b553ae516761c2a22ba2fdd2f37befae3ab6fdab74e9e5193eb", + "image_url": "https://i-invdn-com.investing.com/news/LYNXNPEC4I0AL_L.jpg", + "insights": [ + { + "sentiment": "positive", + "sentiment_reasoning": "UBS analysts are providing a bullish outlook on the extent of future Federal Reserve rate cuts, suggesting that markets are underestimating the number of cuts that will occur.", + "ticker": "UBS" + } + ], + "keywords": [ + "Federal Reserve", + "interest rates", + "economic data" + ], + "published_utc": "2024-06-24T18:33:53Z", + "publisher": { + "favicon_url": "https://s3.polygon.io/public/assets/news/favicons/investing.ico", + "homepage_url": "https://www.investing.com/", + "logo_url": "https://s3.polygon.io/public/assets/news/logos/investing.png", + "name": "Investing.com" + }, + "tickers": [ + "UBS" + ], + "title": "Markets are underestimating Fed cuts: UBS By Investing.com - Investing.com UK" +} \ No newline at end of file diff --git a/components/polygon/sources/new-stock-price-summary/new-stock-price-summary.mjs b/components/polygon/sources/new-stock-price-summary/new-stock-price-summary.mjs new file mode 100644 index 0000000000000..19444274cece9 --- /dev/null +++ b/components/polygon/sources/new-stock-price-summary/new-stock-price-summary.mjs @@ -0,0 +1,54 @@ +import { parseNextPage } from "../../common/utils.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "polygon-new-stock-price-summary", + name: "New Stock Price Summary", + description: "Emit new event when the daily price summary (open, high, low, close) for a specified stock ticker is available.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + stockTicker: { + propDefinition: [ + common.props.app, + "stockTicker", + ], + }, + }, + methods: { + ...common.methods, + getFunction() { + return this.app.getTradeEvents; + }, + getSummary() { + return `Daily price summary for ${this.stockTicker}`; + }, + getData(lastDate) { + return { + stockTicker: this.stockTicker, + params: { + "limit": 5000, + "sort": "timestamp", + "order": "desc", + "timestamp.gt": lastDate, + }, + }; + }, + parseData({ + results, next_url: next, + }) { + const parsedPage = parseNextPage(next); + + return { + parsedData: results, + nextPage: parsedPage, + }; + }, + }, + sampleEmit, +}; + diff --git a/components/polygon/sources/new-stock-price-summary/test-event.mjs b/components/polygon/sources/new-stock-price-summary/test-event.mjs new file mode 100644 index 0000000000000..b3ee9e530df63 --- /dev/null +++ b/components/polygon/sources/new-stock-price-summary/test-event.mjs @@ -0,0 +1,10 @@ +export default { + "T": "AAPL", + "c": 115.97, + "h": 117.59, + "l": 114.13, + "o": 115.55, + "t": 1605042000000, + "v": 131704427, + "vw": 116.3058 +} \ No newline at end of file diff --git a/components/polygon/sources/new-stock-trade/new-stock-trade.mjs b/components/polygon/sources/new-stock-trade/new-stock-trade.mjs new file mode 100644 index 0000000000000..35bc8b292c268 --- /dev/null +++ b/components/polygon/sources/new-stock-trade/new-stock-trade.mjs @@ -0,0 +1,50 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "polygon-new-stock-trade", + name: "New Stock Trade", + description: "Emit new event when a trade occurs for a specified stock ticker.", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + stockTicker: { + propDefinition: [ + common.props.app, + "stockTicker", + ], + }, + adjusted: { + propDefinition: [ + common.props.app, + "adjusted", + ], + optional: true, + }, + }, + methods: { + ...common.methods, + getFunction() { + return this.app.getPreviousClose; + }, + getSummary() { + return `New trade for ${this.stockTicker}`; + }, + getData() { + return { + stockTicker: this.stockTicker, + adjusted: this.adjusted, + }; + }, + parseData({ results }) { + return { + parsedData: results, + nextPage: null, + }; + }, + }, + sampleEmit, +}; diff --git a/components/polygon/sources/new-stock-trade/test-event.mjs b/components/polygon/sources/new-stock-trade/test-event.mjs new file mode 100644 index 0000000000000..2f4ceb47942e1 --- /dev/null +++ b/components/polygon/sources/new-stock-trade/test-event.mjs @@ -0,0 +1,14 @@ +export default { + "conditions": [ + 12, + 41 + ], + "exchange": 11, + "id": "1", + "participant_timestamp": 1517562000015577000, + "price": 171.55, + "sequence_number": 1063, + "sip_timestamp": 1517562000016036600, + "size": 100, + "tape": 3 +} \ No newline at end of file diff --git a/components/portabilling/portabilling.app.mjs b/components/portabilling/portabilling.app.mjs index e2de504d416df..9d2b0ae23fadc 100644 --- a/components/portabilling/portabilling.app.mjs +++ b/components/portabilling/portabilling.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c2c55850ceca..00e2602c6a924 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1479,8 +1479,7 @@ importers: specifier: ^0.0.4 version: 0.0.4 - components/bitbucket_data_center: - specifiers: {} + components/bitbucket_data_center: {} components/bitdefender_gravityzone: {} @@ -4732,8 +4731,7 @@ importers: specifier: ^1.6.0 version: 1.6.6 - components/frontify: - specifiers: {} + components/frontify: {} components/ftrack: dependencies: @@ -7595,8 +7593,7 @@ importers: specifier: ^9.0.0 version: 9.0.1 - components/membership_io: - specifiers: {} + components/membership_io: {} components/memberspot: dependencies: @@ -9508,7 +9505,7 @@ importers: components/polygon: dependencies: '@pipedream/platform': - specifier: ^3.0.0 + specifier: ^3.0.3 version: 3.0.3 components/polygon_io: {} @@ -9535,8 +9532,7 @@ importers: components/popupsmart: {} - components/portabilling: - specifiers: {} + components/portabilling: {} components/portfolio_optimizer: {}