Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 20 additions & 21 deletions packages/backtesting/src/backtesting-report.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { table } from "table";
import type { BotTemplate, IBotConfiguration, Order, SmartTrade } from "@opentrader/bot-processor";
import { ICandlestick, OrderStatusEnum } from "@opentrader/types";
import type { BotTemplate, IBotConfiguration, Order, Trade } from "@opentrader/bot-processor";
import { ICandlestick, XOrderStatus } from "@opentrader/types";
import { format } from "@opentrader/logger";
import { decomposeSymbol } from "@opentrader/tools";
import { buyOrder } from "./report/buyOrder.js";
Expand All @@ -10,14 +10,13 @@ import { sellTransaction } from "./report/sellTransaction.js";
import type { ActiveOrder, Transaction } from "./types/index.js";

type OrderInfo = Order & {
side: "buy" | "sell";
trade: SmartTrade;
trade: Trade;
};

export class BacktestingReport {
constructor(
private candlesticks: ICandlestick[],
private smartTrades: SmartTrade[],
private smartTrades: Trade[],
private botConfig: IBotConfiguration,
private template: BotTemplate<any>,
) {}
Expand All @@ -36,17 +35,17 @@ export class BacktestingReport {
const backtestData: Array<any[]> = [["Date", "Action", "Price", "Quantity", "Amount", "Profit"]];

const trades = this.getOrders().map((order) => {
const amount = order.filledPrice! * order.trade.quantity;
const amount = order.filledPrice! * order.trade.entryOrder.quantity;
const profit =
order.side === "sell" && order.trade.sell
? (order.trade.sell.filledPrice! - order.trade.buy.filledPrice!) * order.trade.quantity
order.side === "Sell" && order.trade.tpOrder
? (order.trade.tpOrder.filledPrice! - order.trade.entryOrder.filledPrice!) * order.trade.entryOrder.quantity
: "-";

return [
format.datetime(order.updatedAt),
order.side.toUpperCase(),
order.filledPrice,
order.trade.quantity,
order.trade.entryOrder.quantity,
amount,
profit,
];
Expand Down Expand Up @@ -83,21 +82,19 @@ Total Profit: ${totalProfit} ${quoteCurrency}

for (const trade of this.getFinishedSmartTrades()) {
orders.push({
...trade.buy,
side: "buy",
...trade.entryOrder,
trade,
});

if (trade.sell) {
if (trade.tpOrder) {
orders.push({
...trade.sell,
side: "sell",
...trade.tpOrder,
trade,
});
}
}

return orders.sort((a, b) => a.updatedAt - b.updatedAt);
return orders.sort((a, b) => a.updatedAt.getTime() - b.updatedAt.getTime());
}

getTransactions(): Transaction[] {
Expand All @@ -108,7 +105,7 @@ Total Profit: ${totalProfit} ${quoteCurrency}
finishedSmartTrades.forEach((smartTrade) => {
transactions.push(buyTransaction(smartTrade));

if (smartTrade.sell) {
if (smartTrade.tpOrder) {
transactions.push(sellTransaction(smartTrade));
}
});
Expand All @@ -124,7 +121,7 @@ Total Profit: ${totalProfit} ${quoteCurrency}
smartTrades.forEach((smartTrade) => {
activeOrders.push(buyOrder(smartTrade));

if (smartTrade.sell) {
if (smartTrade.tpOrder) {
activeOrders.push(sellOrder(smartTrade));
}
});
Expand All @@ -135,8 +132,10 @@ Total Profit: ${totalProfit} ${quoteCurrency}
private calcTotalProfit(): number {
return this.smartTrades.reduce((acc, curr) => {
const priceDiff =
curr.buy.filledPrice && curr.sell?.filledPrice ? curr.sell.filledPrice - curr.buy.filledPrice : 0;
const profit = priceDiff * curr.quantity;
curr.entryOrder.filledPrice && curr.tpOrder?.filledPrice
? curr.tpOrder.filledPrice - curr.entryOrder.filledPrice
: 0;
const profit = priceDiff * curr.entryOrder.quantity;

return acc + profit;
}, 0);
Expand All @@ -145,13 +144,13 @@ Total Profit: ${totalProfit} ${quoteCurrency}
private getActiveSmartTrades() {
return this.smartTrades.filter(
(smartTrade) =>
smartTrade.buy.status === OrderStatusEnum.Placed || smartTrade.sell?.status === OrderStatusEnum.Placed,
smartTrade.entryOrder.status === XOrderStatus.Placed || smartTrade.tpOrder?.status === XOrderStatus.Placed,
);
}

private getFinishedSmartTrades() {
return this.smartTrades.filter((smartTrade) => {
return smartTrade.buy.status === OrderStatusEnum.Filled && smartTrade.sell?.status === OrderStatusEnum.Filled;
return smartTrade.entryOrder.status === XOrderStatus.Filled && smartTrade.tpOrder?.status === XOrderStatus.Filled;
});
}
}
47 changes: 15 additions & 32 deletions packages/backtesting/src/debugging/fulfilledTable.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,34 @@
import type { SmartTrade } from "@opentrader/bot-processor";
import { OrderStatusEnum } from "@opentrader/types";
import type { Trade } from "@opentrader/bot-processor";
import { XOrderStatus } from "@opentrader/types";

export function fulfilledTable(smartTrades: SmartTrade[]) {
export function fulfilledTable(smartTrades: Trade[]) {
const rows = smartTrades.flatMap((smartTrade, i) => {
const { buy, sell } = smartTrade;
const { entryOrder, tpOrder } = smartTrade;

const isBuy =
buy.status === OrderStatusEnum.Placed &&
(!sell || sell.status === OrderStatusEnum.Idle);
const isSell =
buy.status === OrderStatusEnum.Filled &&
sell?.status === OrderStatusEnum.Placed;
const isBuy = entryOrder.status === XOrderStatus.Placed && (!tpOrder || tpOrder.status === XOrderStatus.Idle);
const isSell = entryOrder.status === XOrderStatus.Filled && tpOrder?.status === XOrderStatus.Placed;

const isBuyFilled =
buy.status === OrderStatusEnum.Filled &&
(!sell || sell.status === OrderStatusEnum.Idle);
const isSellFilled =
buy.status === OrderStatusEnum.Filled &&
sell?.status === OrderStatusEnum.Filled;
const isBuyFilled = entryOrder.status === XOrderStatus.Filled && (!tpOrder || tpOrder.status === XOrderStatus.Idle);
const isSellFilled = entryOrder.status === XOrderStatus.Filled && tpOrder?.status === XOrderStatus.Filled;

const prevSmartTrade = smartTrades[i - 1];
const isCurrent =
(isSell && prevSmartTrade?.sell?.status === OrderStatusEnum.Idle) ||
(isSellFilled && prevSmartTrade?.sell?.status === OrderStatusEnum.Idle);
(isSell && prevSmartTrade?.tpOrder?.status === XOrderStatus.Idle) ||
(isSellFilled && prevSmartTrade?.tpOrder?.status === XOrderStatus.Idle);

const side =
isBuy || isBuyFilled
? "buy"
: isSell || isSellFilled
? "sell"
: "unknown";
const side = isBuy || isBuyFilled ? "buy" : isSell || isSellFilled ? "sell" : "unknown";

const price =
side === "sell"
? smartTrade.sell?.price
: side === "buy"
? smartTrade.buy.price
: "unknown";
side === "sell" ? smartTrade.tpOrder?.price : side === "buy" ? smartTrade.entryOrder.price : "unknown";

const gridLine = {
stIndex: i,
ref: smartTrade.ref,
stId: smartTrade.id,
side,
price,
buy: smartTrade.buy.price,
sell: smartTrade.sell?.price,
buy: smartTrade.entryOrder.price,
sell: smartTrade.tpOrder?.price,
filled: isBuyFilled ? "buy filled" : isSellFilled ? "sell filled" : "",
};

Expand All @@ -55,7 +38,7 @@ export function fulfilledTable(smartTrades: SmartTrade[]) {
ref: "-",
stId: "-",
side: "Curr",
price: smartTrade.buy.price,
price: smartTrade.entryOrder.price,
buy: "-",
sell: "-",
filled: "",
Expand Down
31 changes: 11 additions & 20 deletions packages/backtesting/src/debugging/gridTable.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
import type { SmartTrade } from "@opentrader/bot-processor";
import { OrderStatusEnum } from "@opentrader/types";
import type { Trade } from "@opentrader/bot-processor";
import { XOrderStatus } from "@opentrader/types";

export function gridTable(smartTrades: SmartTrade[]) {
export function gridTable(smartTrades: Trade[]) {
const rows = smartTrades.flatMap((smartTrade, i) => {
const { buy, sell } = smartTrade;
const { entryOrder, tpOrder } = smartTrade;

const isBuy =
buy.status === OrderStatusEnum.Placed &&
(!sell || sell.status === OrderStatusEnum.Idle);
const isSell =
buy.status === OrderStatusEnum.Filled &&
sell?.status === OrderStatusEnum.Placed;
const isBuy = entryOrder.status === XOrderStatus.Placed && (!tpOrder || tpOrder.status === XOrderStatus.Idle);
const isSell = entryOrder.status === XOrderStatus.Filled && tpOrder?.status === XOrderStatus.Placed;

const prevSmartTrade = smartTrades[i - 1];
const isCurrent =
isSell && prevSmartTrade?.sell?.status === OrderStatusEnum.Idle;
const isCurrent = isSell && prevSmartTrade?.tpOrder?.status === XOrderStatus.Idle;

const side = isBuy ? "buy" : isSell ? "sell" : "unknown";

const price =
side === "sell"
? smartTrade.sell?.price
: side === "buy"
? smartTrade.buy.price
: "unknown";
side === "sell" ? smartTrade.tpOrder?.price : side === "buy" ? smartTrade.entryOrder.price : "unknown";

const gridLine = {
stIndex: i,
ref: smartTrade.ref,
stId: smartTrade.id,
side,
price,
buy: smartTrade.buy.price,
sell: smartTrade.sell?.price,
buy: smartTrade.entryOrder.price,
sell: smartTrade.tpOrder?.price,
};

if (isCurrent) {
Expand All @@ -41,7 +32,7 @@ export function gridTable(smartTrades: SmartTrade[]) {
ref: "-",
stId: "-",
side: "Curr",
price: smartTrade.buy.price,
price: smartTrade.entryOrder.price,
buy: "-",
sell: "-",
filled: "",
Expand Down
Loading
Loading