-
Notifications
You must be signed in to change notification settings - Fork 20
/
dca.ts
91 lines (74 loc) · 2.6 KB
/
dca.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import { evaluateConditions, extractIndicators, IndicatorsValues, requiredHistory } from "@opentrader/tools";
import { z } from "zod";
import { logger } from "@opentrader/logger";
import { BarSize, XOrderType } from "@opentrader/types";
import {
useDca,
cancelSmartTrade,
IBotConfiguration,
TBotContext,
BotTemplate,
useIndicators,
} from "@opentrader/bot-processor";
export function* dca(ctx: TBotContext<DCABotConfig>) {
const { config, onStart, onStop } = ctx;
const { settings } = config;
if (onStop) {
yield cancelSmartTrade();
logger.info(`[DCA] Bot with ${config.symbol} pair stopped`);
return;
}
if (onStart) {
logger.info(`[DCA] Bot strategy started on ${config.symbol} pair`);
return;
}
const indicators: IndicatorsValues = yield useIndicators(extractIndicators(settings.entry.conditions));
const shouldEntry = evaluateConditions(settings.entry.conditions, indicators);
if (shouldEntry) {
const options = {
price: settings.entry.price,
quantity: settings.entry.quantity,
tpPercent: settings.tp.percent / 100,
safetyOrders: settings.safetyOrders.map((so) => ({
relativePrice: -so.priceDeviation / 100,
quantity: so.quantity,
})),
};
yield useDca(options);
logger.info(options, `[DCA] Entry executed`);
}
logger.info(`[DCA] Strategy executed`);
}
dca.displayName = "DCA Bot";
dca.hidden = true;
dca.schema = z.object({
entry: z.object({
quantity: z.number().positive().describe("Quantity of the Entry Order in base currency"),
type: z.nativeEnum(XOrderType).describe("Entry with Limit or Market order"),
price: z.number().optional(),
conditions: z.any().optional(), // @todo schema validation
}),
tp: z.object({
percent: z.number().positive().describe("Take Profit from entry order price in %"),
}),
safetyOrders: z.array(
z.object({
quantity: z.number().positive().positive("Quantity of the Safety Order in base currency"),
priceDeviation: z.number().positive().positive("Price deviation from the Entry Order price in %"),
}),
),
});
dca.runPolicy = {
onOrderFilled: true,
onCandleClosed: true,
} satisfies Template["runPolicy"];
dca.requiredHistory = (({ settings }) => {
const indicatorsOptions = extractIndicators(settings.entry.conditions);
return requiredHistory(indicatorsOptions);
}) satisfies Template["requiredHistory"];
dca.timeframe = BarSize.ONE_MINUTE;
dca.watchers = {
watchCandles: ({ symbol }: IBotConfiguration) => symbol,
};
type Template = BotTemplate<DCABotConfig>;
export type DCABotConfig = IBotConfiguration<z.infer<typeof dca.schema>>;