Merge branch '15-use-interfaces-instead-of-classes-where-possible' into 'main'
Resolve "Use interfaces instead of classes where possible" Closes #15 See merge request finvis/usxi!14
This commit is contained in:
commit
5450f48820
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@finvis/usxi",
|
"name": "@finvis/usxi",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import Alpaca from '@alpacahq/alpaca-trade-api';
|
import Alpaca from '@alpacahq/alpaca-trade-api';
|
||||||
import { Action, ActionSide, ActionFetchOptions, ActionFetchResponse, ActionDateType } from '../interface/actions';
|
import { ActionSide, ActionFetchOptions, ActionFetchResponse, ActionDateType } from '../interface/actions';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The activity object returned by Alpaca.
|
||||||
|
* @see https://docs.alpaca.markets/reference/getaccountactivitiesbyactivitytype
|
||||||
|
*/
|
||||||
interface AlpacaActivity {
|
interface AlpacaActivity {
|
||||||
id: string;
|
id: string;
|
||||||
activity_type: string;
|
activity_type: string;
|
||||||
@ -29,20 +33,20 @@ export class AlpacaActionProvider {
|
|||||||
pageSize: options.pageSize,
|
pageSize: options.pageSize,
|
||||||
pageToken: undefined,
|
pageToken: undefined,
|
||||||
}) as Promise<AlpacaActivity[]>).then((activities) => {
|
}) as Promise<AlpacaActivity[]>).then((activities) => {
|
||||||
return new ActionFetchResponse(
|
return {
|
||||||
activities
|
actions: activities
|
||||||
.filter((activity) => activity.order_status === "filled")
|
.filter((activity) => activity.order_status === "filled")
|
||||||
.map((activity) => {
|
.map((activity) => {
|
||||||
return new Action(
|
return {
|
||||||
activity.symbol,
|
symbol: activity.symbol,
|
||||||
parseInt(activity.qty, 10),
|
quantity: parseInt(activity.qty, 10),
|
||||||
activity.side === "buy" ? ActionSide.Buy : ActionSide.Sell,
|
side: activity.side === "buy" ? ActionSide.Buy : ActionSide.Sell,
|
||||||
parseFloat(activity.price),
|
pricePerShare: parseFloat(activity.price),
|
||||||
new Date(activity.transaction_time),
|
timestamp: new Date(activity.transaction_time),
|
||||||
);
|
};
|
||||||
}),
|
}),
|
||||||
undefined
|
fetchNextPage: undefined
|
||||||
);
|
};
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
return err;
|
return err;
|
||||||
});
|
});
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
import Alpaca from '@alpacahq/alpaca-trade-api';
|
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.
|
* The position object returned by Alpaca.
|
||||||
* @see https://alpaca.markets/docs/api-references/trading-api/positions/#properties
|
* @see https://alpaca.markets/docs/api-references/trading-api/positions/#properties
|
||||||
*/
|
*/
|
||||||
class AlpacaPosition {
|
interface AlpacaPosition {
|
||||||
asset_id!: string;
|
asset_id: string;
|
||||||
symbol!: string;
|
symbol: string;
|
||||||
exchange!: string;
|
exchange: string;
|
||||||
asset_class!: string;
|
asset_class: string;
|
||||||
avg_entry_price!: string;
|
avg_entry_price: string;
|
||||||
qty!: string;
|
qty: string;
|
||||||
qty_available!: string;
|
qty_available: string;
|
||||||
side!: string;
|
side: string;
|
||||||
market_value!: string;
|
market_value: string;
|
||||||
cost_basis!: string;
|
cost_basis: string;
|
||||||
unrealized_pl!: string;
|
unrealized_pl: string;
|
||||||
unrealized_plpc!: string;
|
unrealized_plpc: string;
|
||||||
unrealized_intraday_pl!: string;
|
unrealized_intraday_pl: string;
|
||||||
unrealized_intraday_plpc!: string;
|
unrealized_intraday_plpc: string;
|
||||||
current_price!: string;
|
current_price: string;
|
||||||
lastday_price!: string;
|
lastday_price: string;
|
||||||
change_today!: string;
|
change_today: string;
|
||||||
asset_marginable!: string;
|
asset_marginable: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,15 +40,17 @@ export class AlpacaPortfolioProvider implements PortfolioProvider {
|
|||||||
*/
|
*/
|
||||||
readonly fetchPortfolio = (): Promise<Portfolio> => {
|
readonly fetchPortfolio = (): Promise<Portfolio> => {
|
||||||
return (this.alpaca.getPositions() as Promise<AlpacaPosition[]>).then((positions) => {
|
return (this.alpaca.getPositions() as Promise<AlpacaPosition[]>).then((positions) => {
|
||||||
return new Portfolio(positions.map((position) => {
|
return {
|
||||||
return new Position(
|
positions: positions.map((position) => {
|
||||||
position.symbol,
|
return {
|
||||||
parseInt(position.qty, 10),
|
symbol: position.symbol,
|
||||||
parseFloat(position.market_value),
|
quantity: parseInt(position.qty, 10),
|
||||||
parseFloat(position.cost_basis),
|
marketValue: parseFloat(position.market_value),
|
||||||
parseFloat(position.market_value)
|
costBasis: parseFloat(position.cost_basis),
|
||||||
);
|
pricePerShare: parseFloat(position.avg_entry_price)
|
||||||
}));
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,14 +17,14 @@ export class AlpacaQuoteProvider implements QuoteProvider {
|
|||||||
*/
|
*/
|
||||||
readonly fetchQuote = (symbol: string): Promise<Quote> => {
|
readonly fetchQuote = (symbol: string): Promise<Quote> => {
|
||||||
return this.alpaca.getLatestQuote(symbol).then((quote) => {
|
return this.alpaca.getLatestQuote(symbol).then((quote) => {
|
||||||
return new Quote(
|
return {
|
||||||
quote.Symbol,
|
symbol: quote.Symbol,
|
||||||
quote.AskPrice,
|
askPrice: quote.AskPrice,
|
||||||
quote.AskSize,
|
askSize: quote.AskSize,
|
||||||
quote.BidPrice,
|
bidPrice: quote.BidPrice,
|
||||||
quote.BidSize,
|
bidSize: quote.BidSize,
|
||||||
new Date(Date.parse(quote.Timestamp))
|
timestamp: new Date(Date.parse(quote.Timestamp))
|
||||||
);
|
};
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
return err;
|
return err;
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,8 @@ export const enum ActionSide {
|
|||||||
/**
|
/**
|
||||||
* Represents an action taken on a stock, such as a buy or sell order.
|
* 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.
|
* The symbol of the asset being traded.
|
||||||
*/
|
*/
|
||||||
@ -34,23 +35,6 @@ export class Action {
|
|||||||
* The timestamp of the action
|
* The timestamp of the action
|
||||||
*/
|
*/
|
||||||
readonly timestamp: Date;
|
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.
|
* Represents options for a date filter in an action fetch.
|
||||||
*/
|
*/
|
||||||
export class ActionDateOptions {
|
export interface ActionDateOptions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The date to filter on.
|
* The date to filter on.
|
||||||
*/
|
*/
|
||||||
@ -76,22 +61,13 @@ export class ActionDateOptions {
|
|||||||
* The type of date filter to use.
|
* The type of date filter to use.
|
||||||
*/
|
*/
|
||||||
readonly dateType: ActionDateType;
|
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.
|
* Represents the options for fetching actions.
|
||||||
*/
|
*/
|
||||||
export class ActionFetchOptions {
|
export interface ActionFetchOptions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of items to fetch per page.
|
* The number of items to fetch per page.
|
||||||
*/
|
*/
|
||||||
@ -101,23 +77,13 @@ export class ActionFetchOptions {
|
|||||||
* The date options for filtering actions.
|
* The date options for filtering actions.
|
||||||
*/
|
*/
|
||||||
readonly dateOptions?: ActionDateOptions;
|
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.
|
* Represents the response of a fetch action request.
|
||||||
*/
|
*/
|
||||||
export class ActionFetchResponse {
|
export interface ActionFetchResponse {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of `Action` objects.
|
* An array of `Action` objects.
|
||||||
*/
|
*/
|
||||||
@ -128,17 +94,6 @@ export class ActionFetchResponse {
|
|||||||
* Returns a promise that resolves to an `ActionFetchResponse` object.
|
* Returns a promise that resolves to an `ActionFetchResponse` object.
|
||||||
*/
|
*/
|
||||||
readonly fetchNextPage?: () => Promise<ActionFetchResponse>;
|
readonly fetchNextPage?: () => Promise<ActionFetchResponse>;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<ActionFetchResponse>) {
|
|
||||||
this.actions = actions;
|
|
||||||
this.fetchNextPage = fetchNextPage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Represents a financial position in a portfolio.
|
* Represents a financial position in a portfolio.
|
||||||
*/
|
*/
|
||||||
export class Position {
|
export interface Position {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The symbol name of the asset
|
* The symbol name of the asset
|
||||||
*/
|
*/
|
||||||
@ -26,43 +27,17 @@ export class Position {
|
|||||||
* The current asset price per share
|
* The current asset price per share
|
||||||
*/
|
*/
|
||||||
readonly pricePerShare: number;
|
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.
|
* Represents a portfolio of financial positions.
|
||||||
*/
|
*/
|
||||||
export class Portfolio {
|
export interface Portfolio {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of positions held in the portfolio.
|
* An array of positions held in the portfolio.
|
||||||
*/
|
*/
|
||||||
readonly positions: Position[];
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,22 +1,37 @@
|
|||||||
/**
|
/**
|
||||||
* Represents a stock quote.
|
* Represents a stock quote.
|
||||||
*/
|
*/
|
||||||
export class Quote {
|
export interface Quote {
|
||||||
readonly symbol: string;
|
|
||||||
readonly askPrice: number;
|
|
||||||
readonly askSize: number;
|
|
||||||
readonly bidPrice: number;
|
|
||||||
readonly bidSize: number;
|
|
||||||
readonly timeStamp: Date;
|
|
||||||
|
|
||||||
constructor(symbol: string, askPrice: number, askSize: number, bidPrice: number, bidSize: number, timeStamp: Date) {
|
/**
|
||||||
this.symbol = symbol;
|
* The symbol of the asset
|
||||||
this.askPrice = askPrice;
|
*/
|
||||||
this.askSize = askSize;
|
readonly symbol: string;
|
||||||
this.bidPrice = bidPrice;
|
|
||||||
this.bidSize = bidSize;
|
/**
|
||||||
this.timeStamp = timeStamp;
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from '@jest/globals';
|
import { describe, expect, test } from '@jest/globals';
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import { ActionDateOptions, ActionDateType, ActionFetchOptions, AlpacaExchange } from '../src/index';
|
import { ActionDateType, AlpacaExchange } from '../src/index';
|
||||||
import { createLogger, transports, format } from "winston";
|
import { createLogger, transports, format } from "winston";
|
||||||
|
|
||||||
const timeout = 10000;
|
const timeout = 10000;
|
||||||
@ -39,9 +39,15 @@ describe('Alpaca Tests', () => {
|
|||||||
expect(process.env.ALPACA_SECRET_KEY).toBeDefined();
|
expect(process.env.ALPACA_SECRET_KEY).toBeDefined();
|
||||||
const exchange = new AlpacaExchange(process.env.ALPACA_API_KEY!, process.env.ALPACA_SECRET_KEY!, true);
|
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();
|
expect(response).toBeDefined();
|
||||||
|
|
||||||
@ -55,9 +61,9 @@ describe('Alpaca Tests', () => {
|
|||||||
expect(action.pricePerShare).toBeDefined();
|
expect(action.pricePerShare).toBeDefined();
|
||||||
|
|
||||||
expect(action.timestamp).toBeDefined();
|
expect(action.timestamp).toBeDefined();
|
||||||
expect(action.timestamp.getFullYear()).toBe(2023);
|
expect(action.timestamp.getFullYear()).toBe(date.getFullYear());
|
||||||
expect(action.timestamp.getMonth()).toBe(9);
|
expect(action.timestamp.getMonth()).toBe(date.getMonth());
|
||||||
expect(action.timestamp.getDate()).toBe(23);
|
expect(action.timestamp.getDate()).toBe(date.getDate());
|
||||||
}
|
}
|
||||||
}, timeout);
|
}, timeout);
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user