Increase fill poll timeout to 2min, always sell after buy
waitForFill now polls 60 times at 2s intervals (2 min total) instead of 30x1s, and uses retry on the getOrder calls. The strategy wraps the hold loop in try/finally so if anything fails after a buy, we still sell the position instead of leaving it orphaned. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -153,17 +153,18 @@ export class Alpaca {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async waitForFill(orderId: string): Promise<AlpacaOrder> {
|
private async waitForFill(orderId: string): Promise<AlpacaOrder> {
|
||||||
const maxAttempts = 30;
|
const maxAttempts = 60;
|
||||||
|
const pollInterval = 2000;
|
||||||
for (let i = 0; i < maxAttempts; i++) {
|
for (let i = 0; i < maxAttempts; i++) {
|
||||||
const order = await this.alpaca.getOrder(orderId);
|
const order = await this.retry('getOrder', () => this.alpaca.getOrder(orderId));
|
||||||
if (order.status === 'filled') return order;
|
if (order.status === 'filled') return order;
|
||||||
if (order.status === 'canceled' || order.status === 'expired' || order.status === 'rejected') {
|
if (order.status === 'canceled' || order.status === 'expired' || order.status === 'rejected') {
|
||||||
throw new Error(`Order ${orderId} ${order.status}`);
|
throw new Error(`Order ${orderId} ${order.status}`);
|
||||||
}
|
}
|
||||||
logger.debug(`order ${orderId} status: ${order.status}, waiting...`);
|
logger.debug(`order ${orderId} status: ${order.status}, waiting...`);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
||||||
}
|
}
|
||||||
throw new Error(`Order ${orderId} not filled after ${maxAttempts}s`);
|
throw new Error(`Order ${orderId} not filled after ${maxAttempts * pollInterval / 1000}s`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async buy(symbol: string, dollarAmount: number): Promise<OrderFill> {
|
public async buy(symbol: string, dollarAmount: number): Promise<OrderFill> {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export class MomentumStrategy implements Strategy {
|
|||||||
const fill = await alpaca.buy(symbol, capitalAmount);
|
const fill = await alpaca.buy(symbol, capitalAmount);
|
||||||
logger.info(`[${this.name}] entered ${symbol} at price ${fill.price}, qty ${fill.qty}`);
|
logger.info(`[${this.name}] entered ${symbol} at price ${fill.price}, qty ${fill.qty}`);
|
||||||
|
|
||||||
|
try {
|
||||||
const targetPrice = fill.price * (1 + this.config.targetGain);
|
const targetPrice = fill.price * (1 + this.config.targetGain);
|
||||||
let deadline = Date.now() + this.config.holdTime;
|
let deadline = Date.now() + this.config.holdTime;
|
||||||
|
|
||||||
@@ -61,7 +62,9 @@ export class MomentumStrategy implements Strategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[${this.name}] exit ${symbol} — reason: ${reason}`);
|
logger.info(`[${this.name}] exit ${symbol} — reason: ${reason}`);
|
||||||
|
} finally {
|
||||||
|
logger.info(`[${this.name}] selling ${fill.qty} shares of ${symbol}`);
|
||||||
await alpaca.sell(symbol, fill.qty);
|
await alpaca.sell(symbol, fill.qty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user