-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathdca.ts
93 lines (76 loc) · 3.24 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
92
93
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.description =
"Dollar-Cost Averaging (DCA) is a trading strategy that involves entering a position through multiple smaller orders, known as Safety Orders. These orders are placed at predetermined levels, below the initial entry price. This method helps reduce the impact of adverse price movements by lowering the overall cost of the position. Once the market reverses and the price reaches a favorable level, the position is closed at the Take Profit level. This strategy is especially effective in volatile markets, allowing traders to capitalize on price fluctuations while minimizing the risks of poor timing with a single large entry.";
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>>;