update our alpaca calls to be separate bid and ask
This commit is contained in:
@@ -7,7 +7,7 @@ function mockClient(overrides: Partial<AlpacaClient> = {}): AlpacaClient {
|
|||||||
getAssets: vi.fn().mockResolvedValue([]),
|
getAssets: vi.fn().mockResolvedValue([]),
|
||||||
getAsset: vi.fn().mockResolvedValue({ symbol: 'TQQQ', fractionable: true, status: 'active', asset_class: 'us_equity' }),
|
getAsset: vi.fn().mockResolvedValue({ symbol: 'TQQQ', fractionable: true, status: 'active', asset_class: 'us_equity' }),
|
||||||
getClock: vi.fn().mockResolvedValue({ is_open: false, next_open: '2025-01-01T14:30:00Z', next_close: '2025-01-01T21:00:00Z' }),
|
getClock: vi.fn().mockResolvedValue({ is_open: false, next_open: '2025-01-01T14:30:00Z', next_close: '2025-01-01T21:00:00Z' }),
|
||||||
getLatestQuote: vi.fn().mockResolvedValue({ ap: 50.00, bp: 49.90 }),
|
getLatestQuote: vi.fn().mockResolvedValue({ AskPrice: 50.00, BidPrice: 49.90 }),
|
||||||
getLatestTrades: vi.fn().mockResolvedValue(new Map()),
|
getLatestTrades: vi.fn().mockResolvedValue(new Map()),
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
};
|
||||||
@@ -41,11 +41,27 @@ describe('Alpaca', () => {
|
|||||||
expect(client.getClock).toHaveBeenCalled();
|
expect(client.getClock).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('delegates getLatestQuote to the underlying client', async () => {
|
it('getLatestAsk returns the ask price', async () => {
|
||||||
const client = mockClient();
|
const client = mockClient();
|
||||||
const alpaca = new Alpaca(false, client);
|
const alpaca = new Alpaca(false, client);
|
||||||
const quote = await alpaca.getLatestQuote('TQQQ');
|
const ask = await alpaca.getLatestAsk('TQQQ');
|
||||||
expect(quote.ap).toBe(50.00);
|
expect(ask).toBe(50.00);
|
||||||
|
expect(client.getLatestQuote).toHaveBeenCalledWith('TQQQ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLatestBid returns the bid price', async () => {
|
||||||
|
const client = mockClient();
|
||||||
|
const alpaca = new Alpaca(false, client);
|
||||||
|
const bid = await alpaca.getLatestBid('TQQQ');
|
||||||
|
expect(bid).toBe(49.90);
|
||||||
|
expect(client.getLatestQuote).toHaveBeenCalledWith('TQQQ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getLatestSpread returns ask minus bid', async () => {
|
||||||
|
const client = mockClient();
|
||||||
|
const alpaca = new Alpaca(false, client);
|
||||||
|
const spread = await alpaca.getLatestSpread('TQQQ');
|
||||||
|
expect(spread).toBeCloseTo(0.10);
|
||||||
expect(client.getLatestQuote).toHaveBeenCalledWith('TQQQ');
|
expect(client.getLatestQuote).toHaveBeenCalledWith('TQQQ');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ export interface AlpacaClock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AlpacaQuote {
|
export interface AlpacaQuote {
|
||||||
ap: number; // ask price
|
AskPrice: number;
|
||||||
bp: number; // bid price
|
BidPrice: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlpacaTrade {
|
export interface AlpacaTrade {
|
||||||
@@ -83,8 +83,17 @@ export class Alpaca {
|
|||||||
return this.alpaca.getClock();
|
return this.alpaca.getClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getLatestQuote(symbol: string) {
|
public async getLatestAsk(symbol: string): Promise<number> {
|
||||||
return this.alpaca.getLatestQuote(symbol);
|
const quote = await this.alpaca.getLatestQuote(symbol);
|
||||||
|
return quote.AskPrice;
|
||||||
|
}
|
||||||
|
public async getLatestBid(symbol: string): Promise<number> {
|
||||||
|
const quote = await this.alpaca.getLatestQuote(symbol);
|
||||||
|
return quote.BidPrice;
|
||||||
|
}
|
||||||
|
public async getLatestSpread(symbol: string): Promise<number> {
|
||||||
|
const quote = await this.alpaca.getLatestQuote(symbol);
|
||||||
|
return quote.AskPrice - quote.BidPrice;
|
||||||
}
|
}
|
||||||
public async getLatestTrades(symbols: string[]) {
|
public async getLatestTrades(symbols: string[]) {
|
||||||
return this.alpaca.getLatestTrades(symbols);
|
return this.alpaca.getLatestTrades(symbols);
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ function mockAlpaca(overrides: Partial<Alpaca> = {}): Alpaca {
|
|||||||
next_open: new Date().toISOString(),
|
next_open: new Date().toISOString(),
|
||||||
next_close: new Date().toISOString(),
|
next_close: new Date().toISOString(),
|
||||||
}),
|
}),
|
||||||
getLatestQuote: vi.fn(),
|
getLatestAsk: vi.fn(),
|
||||||
|
getLatestBid: vi.fn(),
|
||||||
|
getLatestSpread: vi.fn(),
|
||||||
getLatestTrades: vi.fn(),
|
getLatestTrades: vi.fn(),
|
||||||
...overrides,
|
...overrides,
|
||||||
} as unknown as Alpaca;
|
} as unknown as Alpaca;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Alpaca } from "./alpaca";
|
import { Alpaca } from "./alpaca";
|
||||||
import { Strategy } from "./strategy";
|
import { Strategy } from "./strategy";
|
||||||
import { Executor } from "./executor";
|
import { Executor } from "./executor";
|
||||||
import { waitForNextOpen } from "./trading";
|
import { isMarketOpen, waitForNextOpen } from "./trading";
|
||||||
|
|
||||||
export class Bot {
|
export class Bot {
|
||||||
private alpaca: Alpaca;
|
private alpaca: Alpaca;
|
||||||
@@ -21,15 +21,18 @@ export class Bot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async runDay(): Promise<void> {
|
async runDay(): Promise<void> {
|
||||||
|
const open = await isMarketOpen(this.alpaca);
|
||||||
|
if (!open) {
|
||||||
console.log('waiting for open');
|
console.log('waiting for open');
|
||||||
await waitForNextOpen(this.alpaca);
|
await waitForNextOpen(this.alpaca);
|
||||||
|
}
|
||||||
const account = await this.alpaca.getAccount();
|
const account = await this.alpaca.getAccount();
|
||||||
const totalCapital = parseFloat(account.cash);
|
const totalCapital = parseFloat(account.cash);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
this.strategies.map(async (strategy) => {
|
this.strategies.map(async (strategy) => {
|
||||||
const signals = await strategy.execute(this.alpaca);
|
const signals = await strategy.execute(this.alpaca);
|
||||||
|
|
||||||
await this.executor.executeSignals(strategy, signals, totalCapital);
|
await this.executor.executeSignals(strategy, signals, totalCapital);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ function mockAlpaca(): Alpaca {
|
|||||||
getAssets: vi.fn(),
|
getAssets: vi.fn(),
|
||||||
getAsset: vi.fn(),
|
getAsset: vi.fn(),
|
||||||
getClock: vi.fn(),
|
getClock: vi.fn(),
|
||||||
getLatestQuote: vi.fn(),
|
getLatestAsk: vi.fn(),
|
||||||
|
getLatestBid: vi.fn(),
|
||||||
|
getLatestSpread: vi.fn(),
|
||||||
getLatestTrades: vi.fn(),
|
getLatestTrades: vi.fn(),
|
||||||
} as unknown as Alpaca;
|
} as unknown as Alpaca;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ function mockAlpaca(overrides: Partial<Alpaca> = {}): Alpaca {
|
|||||||
getAssets: vi.fn(),
|
getAssets: vi.fn(),
|
||||||
getAsset: vi.fn(),
|
getAsset: vi.fn(),
|
||||||
getClock: vi.fn(),
|
getClock: vi.fn(),
|
||||||
getLatestQuote: vi.fn(),
|
getLatestAsk: vi.fn(),
|
||||||
|
getLatestBid: vi.fn(),
|
||||||
|
getLatestSpread: vi.fn(),
|
||||||
getLatestTrades: vi.fn(),
|
getLatestTrades: vi.fn(),
|
||||||
...overrides,
|
...overrides,
|
||||||
} as unknown as Alpaca;
|
} as unknown as Alpaca;
|
||||||
@@ -26,9 +28,9 @@ afterEach(() => {
|
|||||||
describe('MomentumIndicator', () => {
|
describe('MomentumIndicator', () => {
|
||||||
it('returns up when second quote is higher', async () => {
|
it('returns up when second quote is higher', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
.mockResolvedValueOnce({ ap: 101, bp: 100 }),
|
.mockResolvedValueOnce(101),
|
||||||
});
|
});
|
||||||
|
|
||||||
const indicator = new MomentumIndicator({ settleDelay: 0, sampleDelay: 100 });
|
const indicator = new MomentumIndicator({ settleDelay: 0, sampleDelay: 100 });
|
||||||
@@ -43,9 +45,9 @@ describe('MomentumIndicator', () => {
|
|||||||
|
|
||||||
it('returns down when second quote is lower', async () => {
|
it('returns down when second quote is lower', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
.mockResolvedValueOnce({ ap: 99, bp: 98 }),
|
.mockResolvedValueOnce(99),
|
||||||
});
|
});
|
||||||
|
|
||||||
const indicator = new MomentumIndicator({ settleDelay: 0, sampleDelay: 100 });
|
const indicator = new MomentumIndicator({ settleDelay: 0, sampleDelay: 100 });
|
||||||
@@ -60,9 +62,9 @@ describe('MomentumIndicator', () => {
|
|||||||
|
|
||||||
it('returns up when prices are equal', async () => {
|
it('returns up when prices are equal', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 }),
|
.mockResolvedValueOnce(100),
|
||||||
});
|
});
|
||||||
|
|
||||||
const indicator = new MomentumIndicator({ settleDelay: 0, sampleDelay: 100 });
|
const indicator = new MomentumIndicator({ settleDelay: 0, sampleDelay: 100 });
|
||||||
@@ -74,17 +76,17 @@ describe('MomentumIndicator', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uses configured symbol', async () => {
|
it('uses configured symbol', async () => {
|
||||||
const getLatestQuote = vi.fn()
|
const getLatestAsk = vi.fn()
|
||||||
.mockResolvedValueOnce({ ap: 50, bp: 49 })
|
.mockResolvedValueOnce(50)
|
||||||
.mockResolvedValueOnce({ ap: 51, bp: 50 });
|
.mockResolvedValueOnce(51);
|
||||||
const alpaca = mockAlpaca({ getLatestQuote });
|
const alpaca = mockAlpaca({ getLatestAsk });
|
||||||
|
|
||||||
const indicator = new MomentumIndicator({ symbol: 'SPY', settleDelay: 0, sampleDelay: 100 });
|
const indicator = new MomentumIndicator({ symbol: 'SPY', settleDelay: 0, sampleDelay: 100 });
|
||||||
const promise = indicator.evaluate(alpaca);
|
const promise = indicator.evaluate(alpaca);
|
||||||
await vi.advanceTimersByTimeAsync(100);
|
await vi.advanceTimersByTimeAsync(100);
|
||||||
await promise;
|
await promise;
|
||||||
|
|
||||||
expect(getLatestQuote).toHaveBeenCalledWith('SPY');
|
expect(getLatestAsk).toHaveBeenCalledWith('SPY');
|
||||||
expect(getLatestQuote).toHaveBeenCalledTimes(2);
|
expect(getLatestAsk).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,13 +31,11 @@ export class MomentumIndicator implements Indicator<MomentumResult> {
|
|||||||
async evaluate(alpaca: Alpaca): Promise<MomentumResult> {
|
async evaluate(alpaca: Alpaca): Promise<MomentumResult> {
|
||||||
await wait(this.config.settleDelay);
|
await wait(this.config.settleDelay);
|
||||||
|
|
||||||
const before = await alpaca.getLatestQuote(this.config.symbol);
|
const priceBefore = await alpaca.getLatestAsk(this.config.symbol);
|
||||||
const priceBefore = before.ap;
|
|
||||||
|
|
||||||
await wait(this.config.sampleDelay);
|
await wait(this.config.sampleDelay);
|
||||||
|
|
||||||
const after = await alpaca.getLatestQuote(this.config.symbol);
|
const priceAfter = await alpaca.getLatestAsk(this.config.symbol);
|
||||||
const priceAfter = after.ap;
|
|
||||||
|
|
||||||
const direction = priceAfter >= priceBefore ? 'up' : 'down';
|
const direction = priceAfter >= priceBefore ? 'up' : 'down';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ function mockAlpaca(overrides: Partial<Alpaca> = {}): Alpaca {
|
|||||||
getAssets: vi.fn(),
|
getAssets: vi.fn(),
|
||||||
getAsset: vi.fn(),
|
getAsset: vi.fn(),
|
||||||
getClock: vi.fn(),
|
getClock: vi.fn(),
|
||||||
getLatestQuote: vi.fn(),
|
getLatestAsk: vi.fn(),
|
||||||
|
getLatestBid: vi.fn(),
|
||||||
|
getLatestSpread: vi.fn(),
|
||||||
getLatestTrades: vi.fn(),
|
getLatestTrades: vi.fn(),
|
||||||
...overrides,
|
...overrides,
|
||||||
} as unknown as Alpaca;
|
} as unknown as Alpaca;
|
||||||
@@ -34,15 +36,16 @@ afterEach(() => {
|
|||||||
describe('MomentumStrategy', () => {
|
describe('MomentumStrategy', () => {
|
||||||
it('buys TQQQ when QQQ goes up', async () => {
|
it('buys TQQQ when QQQ goes up', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
// indicator: QQQ before
|
// indicator: QQQ before
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
// indicator: QQQ after (up)
|
// indicator: QQQ after (up)
|
||||||
.mockResolvedValueOnce({ ap: 101, bp: 100 })
|
.mockResolvedValueOnce(101)
|
||||||
// entry quote for TQQQ
|
// entry quote for TQQQ
|
||||||
.mockResolvedValueOnce({ ap: 50, bp: 49 })
|
.mockResolvedValueOnce(50),
|
||||||
|
getLatestBid: vi.fn()
|
||||||
// poll: hit target immediately
|
// poll: hit target immediately
|
||||||
.mockResolvedValueOnce({ ap: 51, bp: 50.50 }),
|
.mockResolvedValueOnce(50.50),
|
||||||
});
|
});
|
||||||
|
|
||||||
const strategy = new MomentumStrategy(1.0, fastConfig);
|
const strategy = new MomentumStrategy(1.0, fastConfig);
|
||||||
@@ -55,15 +58,16 @@ describe('MomentumStrategy', () => {
|
|||||||
|
|
||||||
it('buys SQQQ when QQQ goes down', async () => {
|
it('buys SQQQ when QQQ goes down', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
// indicator: QQQ before
|
// indicator: QQQ before
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
// indicator: QQQ after (down)
|
// indicator: QQQ after (down)
|
||||||
.mockResolvedValueOnce({ ap: 99, bp: 98 })
|
.mockResolvedValueOnce(99)
|
||||||
// entry quote for SQQQ
|
// entry quote for SQQQ
|
||||||
.mockResolvedValueOnce({ ap: 30, bp: 29 })
|
.mockResolvedValueOnce(30),
|
||||||
|
getLatestBid: vi.fn()
|
||||||
// poll: hit target immediately
|
// poll: hit target immediately
|
||||||
.mockResolvedValueOnce({ ap: 31, bp: 30.30 }),
|
.mockResolvedValueOnce(30.30),
|
||||||
});
|
});
|
||||||
|
|
||||||
const strategy = new MomentumStrategy(1.0, fastConfig);
|
const strategy = new MomentumStrategy(1.0, fastConfig);
|
||||||
@@ -76,16 +80,17 @@ describe('MomentumStrategy', () => {
|
|||||||
|
|
||||||
it('sells when bid hits 1% target', async () => {
|
it('sells when bid hits 1% target', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
// indicator: QQQ samples
|
// indicator: QQQ samples
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
.mockResolvedValueOnce({ ap: 101, bp: 100 })
|
.mockResolvedValueOnce(101)
|
||||||
// entry quote: ask = 50
|
// entry quote: ask = 50
|
||||||
.mockResolvedValueOnce({ ap: 50, bp: 49 })
|
.mockResolvedValueOnce(50),
|
||||||
|
getLatestBid: vi.fn()
|
||||||
// poll 1: not yet (target = 50.50)
|
// poll 1: not yet (target = 50.50)
|
||||||
.mockResolvedValueOnce({ ap: 50.20, bp: 50.10 })
|
.mockResolvedValueOnce(50.10)
|
||||||
// poll 2: hit target
|
// poll 2: hit target
|
||||||
.mockResolvedValueOnce({ ap: 51, bp: 50.50 }),
|
.mockResolvedValueOnce(50.50),
|
||||||
});
|
});
|
||||||
|
|
||||||
const strategy = new MomentumStrategy(1.0, fastConfig);
|
const strategy = new MomentumStrategy(1.0, fastConfig);
|
||||||
@@ -99,14 +104,15 @@ describe('MomentumStrategy', () => {
|
|||||||
|
|
||||||
it('sells on timeout when target not reached', async () => {
|
it('sells on timeout when target not reached', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
// indicator: QQQ samples
|
// indicator: QQQ samples
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
.mockResolvedValueOnce({ ap: 101, bp: 100 })
|
.mockResolvedValueOnce(101)
|
||||||
// entry quote: ask = 50
|
// entry quote: ask = 50
|
||||||
.mockResolvedValueOnce({ ap: 50, bp: 49 })
|
.mockResolvedValueOnce(50),
|
||||||
|
getLatestBid: vi.fn()
|
||||||
// all polls: never reach target
|
// all polls: never reach target
|
||||||
.mockResolvedValue({ ap: 50.10, bp: 49.90 }),
|
.mockResolvedValue(49.90),
|
||||||
});
|
});
|
||||||
|
|
||||||
const strategy = new MomentumStrategy(1.0, fastConfig);
|
const strategy = new MomentumStrategy(1.0, fastConfig);
|
||||||
@@ -120,11 +126,12 @@ describe('MomentumStrategy', () => {
|
|||||||
|
|
||||||
it('returns both buy and sell signals', async () => {
|
it('returns both buy and sell signals', async () => {
|
||||||
const alpaca = mockAlpaca({
|
const alpaca = mockAlpaca({
|
||||||
getLatestQuote: vi.fn()
|
getLatestAsk: vi.fn()
|
||||||
.mockResolvedValueOnce({ ap: 100, bp: 99 })
|
.mockResolvedValueOnce(100)
|
||||||
.mockResolvedValueOnce({ ap: 101, bp: 100 })
|
.mockResolvedValueOnce(101)
|
||||||
.mockResolvedValueOnce({ ap: 50, bp: 49 })
|
.mockResolvedValueOnce(50),
|
||||||
.mockResolvedValueOnce({ ap: 51, bp: 50.50 }),
|
getLatestBid: vi.fn()
|
||||||
|
.mockResolvedValueOnce(50.50),
|
||||||
});
|
});
|
||||||
|
|
||||||
const strategy = new MomentumStrategy(1.0, fastConfig);
|
const strategy = new MomentumStrategy(1.0, fastConfig);
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ export class MomentumStrategy implements Strategy {
|
|||||||
const result = await this.indicator.evaluate(alpaca);
|
const result = await this.indicator.evaluate(alpaca);
|
||||||
const symbol = result.direction === 'up' ? 'TQQQ' : 'SQQQ';
|
const symbol = result.direction === 'up' ? 'TQQQ' : 'SQQQ';
|
||||||
|
|
||||||
const entryQuote = await alpaca.getLatestQuote(symbol);
|
const entryPrice = await alpaca.getLatestAsk(symbol);
|
||||||
const entryPrice = entryQuote.ap;
|
|
||||||
|
|
||||||
const buy: Signal = { symbol, direction: 'buy', allocation: 1.0 };
|
const buy: Signal = { symbol, direction: 'buy', allocation: 1.0 };
|
||||||
|
|
||||||
@@ -45,8 +44,8 @@ export class MomentumStrategy implements Strategy {
|
|||||||
while (Date.now() < deadline) {
|
while (Date.now() < deadline) {
|
||||||
await wait(this.config.pollInterval);
|
await wait(this.config.pollInterval);
|
||||||
|
|
||||||
const quote = await alpaca.getLatestQuote(symbol);
|
const bid = await alpaca.getLatestBid(symbol);
|
||||||
if (quote.bp >= targetPrice) {
|
if (bid >= targetPrice) {
|
||||||
reason = 'target';
|
reason = 'target';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ function mockAlpaca(overrides: Partial<Alpaca> = {}): Alpaca {
|
|||||||
getAssets: vi.fn(),
|
getAssets: vi.fn(),
|
||||||
getAsset: vi.fn(),
|
getAsset: vi.fn(),
|
||||||
getClock: vi.fn(),
|
getClock: vi.fn(),
|
||||||
getLatestQuote: vi.fn(),
|
getLatestAsk: vi.fn(),
|
||||||
|
getLatestBid: vi.fn(),
|
||||||
|
getLatestSpread: vi.fn(),
|
||||||
getLatestTrades: vi.fn(),
|
getLatestTrades: vi.fn(),
|
||||||
...overrides,
|
...overrides,
|
||||||
} as unknown as Alpaca;
|
} as unknown as Alpaca;
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ export async function accountBalance(alpaca: Alpaca) {
|
|||||||
return account.cash;
|
return account.cash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function isMarketOpen(alpaca: Alpaca): Promise<boolean> {
|
||||||
|
const clock = await alpaca.getClock();
|
||||||
|
return clock.is_open;
|
||||||
|
}
|
||||||
|
|
||||||
export async function waitForNextOpen(alpaca: Alpaca) {
|
export async function waitForNextOpen(alpaca: Alpaca) {
|
||||||
const clock = await alpaca.getClock();
|
const clock = await alpaca.getClock();
|
||||||
return wait(new Date(clock.next_open).valueOf() - new Date().valueOf());
|
return wait(new Date(clock.next_open).valueOf() - new Date().valueOf());
|
||||||
|
|||||||
Reference in New Issue
Block a user