Replace the Executor logging placeholder with real buy/sell methods on the Alpaca class that place market orders via createOrder and return the fill price. Strategies now receive their capital amount directly and place orders themselves. Bot accepts StrategyAllocation[] to decouple capital allocation from strategy definition. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
139 lines
3.7 KiB
TypeScript
139 lines
3.7 KiB
TypeScript
import dotenv from 'dotenv';
|
|
dotenv.config();
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const AlpacaJS = require('@alpacahq/alpaca-trade-api')
|
|
|
|
if (!process.env.ALPACA_KEY_ID || !process.env.ALPACA_SECRET_KEY) {
|
|
throw new Error('Missing ALPACA_KEY_ID or ALPACA_SECRET_KEY in environment');
|
|
}
|
|
|
|
const paper_credentials = {
|
|
keyId : process.env.ALPACA_KEY_ID,
|
|
secretKey : process.env.ALPACA_SECRET_KEY,
|
|
paper : true
|
|
}
|
|
|
|
export interface AlpacaAccount {
|
|
cash: string;
|
|
}
|
|
|
|
export interface AlpacaAsset {
|
|
symbol: string;
|
|
fractionable: boolean;
|
|
status: string;
|
|
asset_class: string;
|
|
}
|
|
|
|
export interface AlpacaClock {
|
|
is_open: boolean;
|
|
next_open: string;
|
|
next_close: string;
|
|
}
|
|
|
|
export interface AlpacaQuote {
|
|
AskPrice: number;
|
|
BidPrice: number;
|
|
}
|
|
|
|
export interface AlpacaOrder {
|
|
id: string;
|
|
symbol: string;
|
|
filled_avg_price: string;
|
|
filled_qty: string;
|
|
side: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface AlpacaTrade {
|
|
p: number; // price
|
|
s: number; // size
|
|
t: string; // timestamp
|
|
}
|
|
|
|
export interface AlpacaClient {
|
|
getAccount(): Promise<AlpacaAccount>;
|
|
getAssets(params: { status: string; asset_class: string }): Promise<AlpacaAsset[]>;
|
|
getAsset(symbol: string): Promise<AlpacaAsset>;
|
|
getClock(): Promise<AlpacaClock>;
|
|
getLatestQuote(symbol: string): Promise<AlpacaQuote>;
|
|
getLatestTrades(symbols: string[]): Promise<Map<string, AlpacaTrade>>;
|
|
createOrder(order: {
|
|
symbol: string;
|
|
notional: number;
|
|
side: 'buy' | 'sell';
|
|
type: string;
|
|
time_in_force: string;
|
|
}): Promise<AlpacaOrder>;
|
|
}
|
|
|
|
export class Alpaca {
|
|
private alpaca: AlpacaClient;
|
|
constructor(live = false, client?: AlpacaClient) {
|
|
if (client) {
|
|
this.alpaca = client;
|
|
}
|
|
else if (live) {
|
|
throw new Error("not doing live yet");
|
|
}
|
|
else {
|
|
this.alpaca = new AlpacaJS(paper_credentials);
|
|
}
|
|
}
|
|
|
|
public async getAccount() {
|
|
return this.alpaca.getAccount();
|
|
}
|
|
public async getAssets() {
|
|
|
|
return this.alpaca.getAssets({
|
|
status : 'active',
|
|
asset_class : 'us_equity'
|
|
});
|
|
}
|
|
public async getAsset(symbol: string) {
|
|
return this.alpaca.getAsset(symbol);
|
|
}
|
|
|
|
public async getClock() {
|
|
return this.alpaca.getClock();
|
|
}
|
|
|
|
public async getLatestAsk(symbol: string): Promise<number> {
|
|
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[]) {
|
|
return this.alpaca.getLatestTrades(symbols);
|
|
}
|
|
|
|
public async buy(symbol: string, dollarAmount: number): Promise<number> {
|
|
const order = await this.alpaca.createOrder({
|
|
symbol,
|
|
notional: dollarAmount,
|
|
side: 'buy',
|
|
type: 'market',
|
|
time_in_force: 'day',
|
|
});
|
|
return parseFloat(order.filled_avg_price);
|
|
}
|
|
|
|
public async sell(symbol: string, dollarAmount: number): Promise<number> {
|
|
const order = await this.alpaca.createOrder({
|
|
symbol,
|
|
notional: dollarAmount,
|
|
side: 'sell',
|
|
type: 'market',
|
|
time_in_force: 'day',
|
|
});
|
|
return parseFloat(order.filled_avg_price);
|
|
}
|
|
} |