From 65d6756582126fbeb397061cc334463f4e5a5c74 Mon Sep 17 00:00:00 2001 From: Carter Bertolini Date: Fri, 10 Nov 2023 16:45:49 -0500 Subject: [PATCH 1/4] Replace classes with interfaces in action. --- src/alpaca/actions.ts | 24 ++++++++-------- src/interface/actions.ts | 61 ++++++---------------------------------- test/alpaca.test.ts | 18 ++++++++---- 3 files changed, 32 insertions(+), 71 deletions(-) diff --git a/src/alpaca/actions.ts b/src/alpaca/actions.ts index 471f6dc..1be98b9 100644 --- a/src/alpaca/actions.ts +++ b/src/alpaca/actions.ts @@ -1,5 +1,5 @@ import Alpaca from '@alpacahq/alpaca-trade-api'; -import { Action, ActionSide, ActionFetchOptions, ActionFetchResponse, ActionDateType } from '../interface/actions'; +import { ActionSide, ActionFetchOptions, ActionFetchResponse, ActionDateType } from '../interface/actions'; interface AlpacaActivity { id: string; @@ -29,20 +29,20 @@ export class AlpacaActionProvider { pageSize: options.pageSize, pageToken: undefined, }) as Promise).then((activities) => { - return new ActionFetchResponse( - activities + return { + actions: activities .filter((activity) => activity.order_status === "filled") .map((activity) => { - return new Action( - activity.symbol, - parseInt(activity.qty, 10), - activity.side === "buy" ? ActionSide.Buy : ActionSide.Sell, - parseFloat(activity.price), - new Date(activity.transaction_time), - ); + return { + symbol: activity.symbol, + quantity: parseInt(activity.qty, 10), + side: activity.side === "buy" ? ActionSide.Buy : ActionSide.Sell, + pricePerShare: parseFloat(activity.price), + timestamp: new Date(activity.transaction_time), + }; }), - undefined - ); + fetchNextPage: undefined + }; }).catch((err) => { return err; }); diff --git a/src/interface/actions.ts b/src/interface/actions.ts index 91ca388..e03b8af 100644 --- a/src/interface/actions.ts +++ b/src/interface/actions.ts @@ -9,7 +9,8 @@ export const enum ActionSide { /** * Represents an action taken on a stock, such as a buy or sell order. */ -export class Action { +export interface Action { + /** * The symbol of the asset being traded. */ @@ -34,23 +35,6 @@ export class Action { * The timestamp of the action */ readonly timestamp: Date; - - /** - * Represents a user Action. - * @constructor - * @param {string} symbol - The symbol of the asset being traded. - * @param {number} quantity - The quantity of the asset being traded. - * @param {ActionSide} side - The side of the trade (buy or sell). - * @param {number} pricePerShare - The price per share of the asset being traded. - * @param {Date} timestamp - The timestamp of the action. - */ - constructor(symbol: string, quantity: number, side: ActionSide, pricePerShare: number, timestamp: Date) { - this.symbol = symbol; - this.quantity = quantity; - this.side = side; - this.pricePerShare = pricePerShare; - this.timestamp = timestamp; - } } /** @@ -66,7 +50,8 @@ export const enum ActionDateType { /** * Represents options for a date filter in an action fetch. */ -export class ActionDateOptions { +export interface ActionDateOptions { + /** * The date to filter on. */ @@ -76,22 +61,13 @@ export class ActionDateOptions { * The type of date filter to use. */ readonly dateType: ActionDateType; - - /** - * Creates a new ActionDateOptions instance. - * @param date The date to filter on. - * @param dateType The type of date filter to use. - */ - constructor(date: Date, dateType: ActionDateType) { - this.date = date; - this.dateType = dateType; - } } /** * Represents the options for fetching actions. */ -export class ActionFetchOptions { +export interface ActionFetchOptions { + /** * The number of items to fetch per page. */ @@ -101,23 +77,13 @@ export class ActionFetchOptions { * The date options for filtering actions. */ readonly dateOptions?: ActionDateOptions; - - /** - * Creates a set of options for an Action fetch. - * @constructor - * @param pageSize - The size of the page if paging is desired. - * @param dateOptions - The options for Date filtering. - */ - constructor(pageSize?: number, dateOptions?: ActionDateOptions) { - this.pageSize = pageSize; - this.dateOptions = dateOptions; - } } /** * Represents the response of a fetch action request. */ -export class ActionFetchResponse { +export interface ActionFetchResponse { + /** * An array of `Action` objects. */ @@ -128,17 +94,6 @@ export class ActionFetchResponse { * Returns a promise that resolves to an `ActionFetchResponse` object. */ readonly fetchNextPage?: () => Promise; - - /** - * Creates an instance of the Actions class. - * @constructor - * @param actions The list of actions. - * @param fetchNextPage A function that fetches the next page of actions. - */ - constructor(actions: Action[], fetchNextPage?: () => Promise) { - this.actions = actions; - this.fetchNextPage = fetchNextPage; - } } /** diff --git a/test/alpaca.test.ts b/test/alpaca.test.ts index 9c23980..eff46d5 100644 --- a/test/alpaca.test.ts +++ b/test/alpaca.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from '@jest/globals'; import 'dotenv/config'; -import { ActionDateOptions, ActionDateType, ActionFetchOptions, AlpacaExchange } from '../src/index'; +import { ActionDateType, AlpacaExchange } from '../src/index'; import { createLogger, transports, format } from "winston"; const timeout = 10000; @@ -39,9 +39,15 @@ describe('Alpaca Tests', () => { expect(process.env.ALPACA_SECRET_KEY).toBeDefined(); const exchange = new AlpacaExchange(process.env.ALPACA_API_KEY!, process.env.ALPACA_SECRET_KEY!, true); - const fetchOptions = new ActionFetchOptions(undefined, new ActionDateOptions(new Date("2023-10-23T13:30:28.163Z"), ActionDateType.On)); + const date = new Date("2023-10-23T13:30:28.163Z"); - const response = await exchange.actionProvider.fetchActions(fetchOptions); + const response = await exchange.actionProvider.fetchActions({ + pageSize: undefined, + dateOptions: { + date: date, + dateType: ActionDateType.On + } + }); expect(response).toBeDefined(); @@ -55,9 +61,9 @@ describe('Alpaca Tests', () => { expect(action.pricePerShare).toBeDefined(); expect(action.timestamp).toBeDefined(); - expect(action.timestamp.getFullYear()).toBe(2023); - expect(action.timestamp.getMonth()).toBe(9); - expect(action.timestamp.getDate()).toBe(23); + expect(action.timestamp.getFullYear()).toBe(date.getFullYear()); + expect(action.timestamp.getMonth()).toBe(date.getMonth()); + expect(action.timestamp.getDate()).toBe(date.getDate()); } }, timeout); }); -- 2.47.2 From 97d808bca2848d228ffe99a4d53d8c5fdb4768ae Mon Sep 17 00:00:00 2001 From: Carter Bertolini Date: Fri, 10 Nov 2023 16:51:14 -0500 Subject: [PATCH 2/4] Replace class with interface in portfolio. --- src/alpaca/portfolio.ts | 58 ++++++++++++++++++++------------------ src/interface/portfolio.ts | 31 ++------------------ 2 files changed, 32 insertions(+), 57 deletions(-) diff --git a/src/alpaca/portfolio.ts b/src/alpaca/portfolio.ts index 728513f..105d68a 100644 --- a/src/alpaca/portfolio.ts +++ b/src/alpaca/portfolio.ts @@ -5,25 +5,25 @@ import { PortfolioProvider, Portfolio, Position } from '../interface/portfolio'; * The position object returned by Alpaca. * @see https://alpaca.markets/docs/api-references/trading-api/positions/#properties */ -class AlpacaPosition { - asset_id!: string; - symbol!: string; - exchange!: string; - asset_class!: string; - avg_entry_price!: string; - qty!: string; - qty_available!: string; - side!: string; - market_value!: string; - cost_basis!: string; - unrealized_pl!: string; - unrealized_plpc!: string; - unrealized_intraday_pl!: string; - unrealized_intraday_plpc!: string; - current_price!: string; - lastday_price!: string; - change_today!: string; - asset_marginable!: string; +interface AlpacaPosition { + asset_id: string; + symbol: string; + exchange: string; + asset_class: string; + avg_entry_price: string; + qty: string; + qty_available: string; + side: string; + market_value: string; + cost_basis: string; + unrealized_pl: string; + unrealized_plpc: string; + unrealized_intraday_pl: string; + unrealized_intraday_plpc: string; + current_price: string; + lastday_price: string; + change_today: string; + asset_marginable: string; } /** @@ -40,15 +40,17 @@ export class AlpacaPortfolioProvider implements PortfolioProvider { */ readonly fetchPortfolio = (): Promise => { return (this.alpaca.getPositions() as Promise).then((positions) => { - return new Portfolio(positions.map((position) => { - return new Position( - position.symbol, - parseInt(position.qty, 10), - parseFloat(position.market_value), - parseFloat(position.cost_basis), - parseFloat(position.market_value) - ); - })); + return { + positions: positions.map((position) => { + return { + symbol: position.symbol, + quantity: parseInt(position.qty, 10), + marketValue: parseFloat(position.market_value), + costBasis: parseFloat(position.cost_basis), + pricePerShare: parseFloat(position.avg_entry_price) + } + }) + }; }); }; diff --git a/src/interface/portfolio.ts b/src/interface/portfolio.ts index 1b06baa..0d26058 100644 --- a/src/interface/portfolio.ts +++ b/src/interface/portfolio.ts @@ -1,7 +1,7 @@ /** * Represents a financial position in a portfolio. */ -export class Position { +export interface Position { /** * The symbol name of the asset */ @@ -26,43 +26,16 @@ export class Position { * The current asset price per share */ readonly pricePerShare: number; - - - /** - * Creates a new Portfolio object. - * @constructor - * @param {string} symbol - The symbol of the asset in the portfolio. - * @param {number} quantity - The quantity of the asset in the portfolio. - * @param {number} marketValue - The market value of the asset in the portfolio. - * @param {number} costBasis - The cost basis of the asset in the portfolio. - * @param {number} pricePerShare - The price per share of the asset in the portfolio. - */ - constructor(symbol: string, quantity: number, marketValue: number, costBasis: number, pricePerShare: number) { - this.symbol = symbol; - this.quantity = quantity; - this.marketValue = marketValue; - this.costBasis = costBasis; - this.pricePerShare = pricePerShare; - } } /** * Represents a portfolio of financial positions. */ -export class Portfolio { +export interface Portfolio { /** * An array of positions held in the portfolio. */ readonly positions: Position[]; - - /** - * Creates a new Portfolio instance. - * @constructor - * @param {Position[]} positions - An array of Position objects representing the positions in the portfolio. - */ - constructor(positions: Position[]) { - this.positions = positions; - } } /** -- 2.47.2 From 986a0ab5690def708ee325d248de13328ddccb6e Mon Sep 17 00:00:00 2001 From: Carter Bertolini Date: Fri, 10 Nov 2023 17:23:30 -0500 Subject: [PATCH 3/4] Make quote and portfolio interfaces. --- src/alpaca/actions.ts | 4 ++++ src/alpaca/portfolio.ts | 4 ++-- src/alpaca/quote.ts | 16 +++++++------- src/interface/portfolio.ts | 2 ++ src/interface/quote.ts | 45 +++++++++++++++++++++++++------------- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/alpaca/actions.ts b/src/alpaca/actions.ts index 1be98b9..d71cdb1 100644 --- a/src/alpaca/actions.ts +++ b/src/alpaca/actions.ts @@ -1,6 +1,10 @@ import Alpaca from '@alpacahq/alpaca-trade-api'; import { ActionSide, ActionFetchOptions, ActionFetchResponse, ActionDateType } from '../interface/actions'; +/** + * The activity object returned by Alpaca. + * @see https://docs.alpaca.markets/reference/getaccountactivitiesbyactivitytype + */ interface AlpacaActivity { id: string; activity_type: string; diff --git a/src/alpaca/portfolio.ts b/src/alpaca/portfolio.ts index 105d68a..03cf3fc 100644 --- a/src/alpaca/portfolio.ts +++ b/src/alpaca/portfolio.ts @@ -1,5 +1,5 @@ import Alpaca from '@alpacahq/alpaca-trade-api'; -import { PortfolioProvider, Portfolio, Position } from '../interface/portfolio'; +import { PortfolioProvider, Portfolio } from '../interface/portfolio'; /** * The position object returned by Alpaca. @@ -48,7 +48,7 @@ export class AlpacaPortfolioProvider implements PortfolioProvider { marketValue: parseFloat(position.market_value), costBasis: parseFloat(position.cost_basis), pricePerShare: parseFloat(position.avg_entry_price) - } + }; }) }; }); diff --git a/src/alpaca/quote.ts b/src/alpaca/quote.ts index 574a106..7b0e6bd 100644 --- a/src/alpaca/quote.ts +++ b/src/alpaca/quote.ts @@ -17,14 +17,14 @@ export class AlpacaQuoteProvider implements QuoteProvider { */ readonly fetchQuote = (symbol: string): Promise => { return this.alpaca.getLatestQuote(symbol).then((quote) => { - return new Quote( - quote.Symbol, - quote.AskPrice, - quote.AskSize, - quote.BidPrice, - quote.BidSize, - new Date(Date.parse(quote.Timestamp)) - ); + return { + symbol: quote.Symbol, + askPrice: quote.AskPrice, + askSize: quote.AskSize, + bidPrice: quote.BidPrice, + bidSize: quote.BidSize, + timestamp: new Date(Date.parse(quote.Timestamp)) + }; }).catch((err) => { return err; }); diff --git a/src/interface/portfolio.ts b/src/interface/portfolio.ts index 0d26058..e20e152 100644 --- a/src/interface/portfolio.ts +++ b/src/interface/portfolio.ts @@ -2,6 +2,7 @@ * Represents a financial position in a portfolio. */ export interface Position { + /** * The symbol name of the asset */ @@ -32,6 +33,7 @@ export interface Position { * Represents a portfolio of financial positions. */ export interface Portfolio { + /** * An array of positions held in the portfolio. */ diff --git a/src/interface/quote.ts b/src/interface/quote.ts index 4449bb8..2856632 100644 --- a/src/interface/quote.ts +++ b/src/interface/quote.ts @@ -1,22 +1,37 @@ /** * Represents a stock quote. */ -export class Quote { - readonly symbol: string; - readonly askPrice: number; - readonly askSize: number; - readonly bidPrice: number; - readonly bidSize: number; - readonly timeStamp: Date; +export interface Quote { - constructor(symbol: string, askPrice: number, askSize: number, bidPrice: number, bidSize: number, timeStamp: Date) { - this.symbol = symbol; - this.askPrice = askPrice; - this.askSize = askSize; - this.bidPrice = bidPrice; - this.bidSize = bidSize; - this.timeStamp = timeStamp; - } + /** + * The symbol of the asset + */ + readonly symbol: string; + + /** + * The ask price of the asset + */ + readonly askPrice: number; + + /** + * The ask size of the asset + */ + readonly askSize: number; + + /** + * The bid price of the asset + */ + readonly bidPrice: number; + + /** + * The bid size of the asset + */ + readonly bidSize: number; + + /** + * The timestamp of the quote + */ + readonly timeStamp: Date; } /** -- 2.47.2 From 077deb508bc3da12f9573222448ee723b05ca3fa Mon Sep 17 00:00:00 2001 From: Carter Bertolini Date: Fri, 10 Nov 2023 17:23:50 -0500 Subject: [PATCH 4/4] Bump package version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b2da7b3..72f571e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@finvis/usxi", - "version": "1.0.0", + "version": "1.1.0", "description": "", "main": "index.js", "scripts": { -- 2.47.2