Skip to content

Commit

Permalink
feat: improve processing
Browse files Browse the repository at this point in the history
* Added `SmartTradeExecutor` and `Order`

* SmartTrade now supports optional TP

* Added `buy, sell, useTrade` effects

* The `useExchange()` now can use external exchanges by passing exchange label

* Refactored `bot-processor` effects
  • Loading branch information
bludnic committed May 16, 2024
1 parent 19d253a commit fa16fb4
Show file tree
Hide file tree
Showing 84 changed files with 1,714 additions and 707 deletions.
14 changes: 10 additions & 4 deletions packages/backtesting/src/backtesting-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export class BacktestingReport {

finishedSmartTrades.forEach((smartTrade) => {
transactions.push(buyTransaction(smartTrade));
transactions.push(sellTransaction(smartTrade));

if (smartTrade.sell) {
transactions.push(sellTransaction(smartTrade));
}
});

return transactions;
Expand All @@ -37,7 +40,10 @@ export class BacktestingReport {

smartTrades.forEach((smartTrade) => {
activeOrders.push(buyOrder(smartTrade));
activeOrders.push(sellOrder(smartTrade));

if (smartTrade.sell) {
activeOrders.push(sellOrder(smartTrade));
}
});

return activeOrders;
Expand All @@ -57,15 +63,15 @@ export class BacktestingReport {
return this.smartTrades.filter(
(smartTrade) =>
smartTrade.buy.status === OrderStatusEnum.Placed ||
smartTrade.sell.status === OrderStatusEnum.Placed,
smartTrade.sell?.status === OrderStatusEnum.Placed,
);
}

private getFinishedSmartTrades() {
return this.smartTrades.filter((smartTrade) => {
return (
smartTrade.buy.status === OrderStatusEnum.Filled &&
smartTrade.sell.status === OrderStatusEnum.Filled
smartTrade.sell?.status === OrderStatusEnum.Filled
);
});
}
Expand Down
8 changes: 4 additions & 4 deletions packages/backtesting/src/backtesting.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type {
IBotConfiguration,
BotManager,
StrategyRunner,
BotTemplate,
} from "@opentrader/bot-processor";
import { BotProcessor } from "@opentrader/bot-processor";
import { createStrategyRunner } from "@opentrader/bot-processor";
import type { ICandlestick } from "@opentrader/types";
import { logger, format } from "@opentrader/logger";
import { fulfilledTable, gridTable } from "./debugging";
Expand All @@ -17,7 +17,7 @@ export class Backtesting<T extends IBotConfiguration<T>> {
private marketSimulator: MarketSimulator;
private store: MemoryStore;
private exchange: MemoryExchange;
private processor: BotManager<T>;
private processor: StrategyRunner<T>;

constructor(options: { botConfig: T; botTemplate: BotTemplate<T> }) {
const { botConfig, botTemplate } = options;
Expand All @@ -26,7 +26,7 @@ export class Backtesting<T extends IBotConfiguration<T>> {
this.store = new MemoryStore(this.marketSimulator);
this.exchange = new MemoryExchange(this.marketSimulator);

this.processor = BotProcessor.create({
this.processor = createStrategyRunner({
store: this.store,
exchange: this.exchange,
botConfig,
Expand Down
12 changes: 6 additions & 6 deletions packages/backtesting/src/debugging/fulfilledTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ export function fulfilledTable(smartTrades: SmartTrade[]) {

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

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

const prevSmartTrade = smartTrades[i - 1];
const isCurrent =
Expand All @@ -33,7 +33,7 @@ export function fulfilledTable(smartTrades: SmartTrade[]) {

const price =
side === "sell"
? smartTrade.sell.price
? smartTrade.sell?.price
: side === "buy"
? smartTrade.buy.price
: "unknown";
Expand All @@ -45,7 +45,7 @@ export function fulfilledTable(smartTrades: SmartTrade[]) {
side,
price,
buy: smartTrade.buy.price,
sell: smartTrade.sell.price,
sell: smartTrade.sell?.price,
filled: isBuyFilled ? "buy filled" : isSellFilled ? "sell filled" : "",
};

Expand Down
8 changes: 4 additions & 4 deletions packages/backtesting/src/debugging/gridTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export function gridTable(smartTrades: SmartTrade[]) {

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

const prevSmartTrade = smartTrades[i - 1];
const isCurrent =
Expand All @@ -20,7 +20,7 @@ export function gridTable(smartTrades: SmartTrade[]) {

const price =
side === "sell"
? smartTrade.sell.price
? smartTrade.sell?.price
: side === "buy"
? smartTrade.buy.price
: "unknown";
Expand All @@ -32,7 +32,7 @@ export function gridTable(smartTrades: SmartTrade[]) {
side,
price,
buy: smartTrade.buy.price,
sell: smartTrade.sell.price,
sell: smartTrade.sell?.price,
};

if (isCurrent) {
Expand Down
4 changes: 2 additions & 2 deletions packages/backtesting/src/report/sellOrder.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { SmartTrade } from "@opentrader/bot-processor";
import { SmartTradeWithSell } from "@opentrader/bot-processor";
import { OrderSideEnum } from "@opentrader/types";
import type { ActiveOrder } from "../types";

export function sellOrder(smartTrade: SmartTrade): ActiveOrder {
export function sellOrder(smartTrade: SmartTradeWithSell): ActiveOrder {
return {
side: OrderSideEnum.Sell,
quantity: smartTrade.quantity,
Expand Down
6 changes: 4 additions & 2 deletions packages/backtesting/src/report/sellTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { SmartTrade } from "@opentrader/bot-processor";
import type { SmartTradeWithSell } from "@opentrader/bot-processor";
import { OrderSideEnum } from "@opentrader/types";
import type { SellTransaction } from "../types";

export function sellTransaction(smartTrade: SmartTrade): SellTransaction {
export function sellTransaction(
smartTrade: SmartTradeWithSell,
): SellTransaction {
const { buy, sell, quantity, id } = smartTrade;

return {
Expand Down
60 changes: 54 additions & 6 deletions packages/backtesting/src/store/memory-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
SmartTrade,
UseSmartTradePayload,
} from "@opentrader/bot-processor";
import type { IExchange } from "@opentrader/exchanges";
import { OrderStatusEnum } from "@opentrader/types";
import uniqueId from "lodash/uniqueId";
import type { MarketSimulator } from "../market-simulator";
Expand Down Expand Up @@ -61,12 +62,14 @@ export class MemoryStore implements IStore {
createdAt,
updatedAt: createdAt,
},
sell: {
price: sell.price,
status: sell.status || OrderStatusEnum.Idle,
createdAt,
updatedAt: createdAt,
},
sell: sell
? {
price: sell.price,
status: sell.status || OrderStatusEnum.Idle,
createdAt,
updatedAt: createdAt,
}
: undefined,
quantity,
};

Expand All @@ -75,8 +78,53 @@ export class MemoryStore implements IStore {
return smartTrade;
}

async updateSmartTrade(
ref: string,
payload: Pick<UseSmartTradePayload, "sell">,
botId: number,
) {
if (!payload.sell) {
console.log(
"MemoryStore: Unable to update smart trade. Reason: `payload.sell` not provided.",
);
return null;
}

const smartTrade = await this.getSmartTrade(ref, botId);

if (!smartTrade) {
return null;
}

const candlestick = this.marketSimulator.currentCandle;
const updatedAt = candlestick.timestamp;

if (smartTrade.sell) {
console.log(
"MemoryStore: SmartTrade already has a sell order. Skipping.",
);
return smartTrade;
}

const updatedSmartTrade: SmartTrade = {
...smartTrade,
sell: {
price: payload.sell.price,
status: payload.sell.status || OrderStatusEnum.Idle,
createdAt: updatedAt,
updatedAt,
},
};

return updatedSmartTrade;
}

async cancelSmartTrade(_ref: string, _botId: number): Promise<boolean> {
return false; // @todo
// throw new Error("Not implemented yet.");
}

async getExchange(_label: string): Promise<IExchange | null> {
throw new Error("Not implemented yet.");
}
}
33 changes: 24 additions & 9 deletions packages/bot-processor/src/bot-control.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { OrderStatusEnum } from "@opentrader/types";
import type { UseSmartTradePayload } from "./effects/common/types/use-smart-trade-effect";
import type { IBotConfiguration } from "./types/bot/bot-configuration.interface";
import type { IBotControl } from "./types/bot/bot-control.interface";
import type { SmartTrade } from "./types/smart-trade/smart-trade.type";
import type { IStore } from "./types/store/store.interface";
import type { UseSmartTradePayload } from "./effects";
import type {
IBotConfiguration,
IBotControl,
SmartTrade,
IStore,
} from "./types";

export class BotControl<T extends IBotConfiguration> implements IBotControl {
constructor(
Expand All @@ -23,6 +25,13 @@ export class BotControl<T extends IBotConfiguration> implements IBotControl {
return this.store.createSmartTrade(ref, payload, this.bot.id);
}

async updateSmartTrade(
ref: string,
payload: Pick<UseSmartTradePayload, "sell">,
) {
return this.store.updateSmartTrade(ref, payload, this.bot.id);
}

async getOrCreateSmartTrade(
ref: string,
payload: UseSmartTradePayload,
Expand All @@ -45,10 +54,12 @@ export class BotControl<T extends IBotConfiguration> implements IBotControl {
price: smartTrade.buy.price,
status: OrderStatusEnum.Idle,
},
sell: {
price: smartTrade.sell.price,
status: OrderStatusEnum.Idle,
},
sell: smartTrade.sell
? {
price: smartTrade.sell.price,
status: OrderStatusEnum.Idle,
}
: undefined,
quantity: smartTrade.quantity,
};

Expand All @@ -58,4 +69,8 @@ export class BotControl<T extends IBotConfiguration> implements IBotControl {
async cancelSmartTrade(ref: string) {
return this.store.cancelSmartTrade(ref, this.bot.id);
}

async getExchange(label: string) {
return this.store.getExchange(label);
}
}
Loading

0 comments on commit fa16fb4

Please sign in to comment.