@gbozee/ultimate 0.0.2-130 → 0.0.2-131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/frontend-index.d.ts +27 -0
- package/dist/frontend-index.js +47 -0
- package/dist/index.cjs +188 -1
- package/dist/index.d.ts +91 -0
- package/dist/index.js +188 -1
- package/dist/mcp-server.cjs +187 -1
- package/dist/mcp-server.js +187 -1
- package/package.json +2 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -599,6 +599,33 @@ export declare function getOptimumHedgeFactor(payload: {
|
|
|
599
599
|
iterations: number;
|
|
600
600
|
converged: boolean;
|
|
601
601
|
};
|
|
602
|
+
export type CType = {
|
|
603
|
+
next_order: number;
|
|
604
|
+
take_profit: number;
|
|
605
|
+
};
|
|
606
|
+
export declare function determineCompoundLongTrade(payload: {
|
|
607
|
+
focus_short_position: CType;
|
|
608
|
+
focus_long_position: CType;
|
|
609
|
+
shortConfig: {
|
|
610
|
+
entry: number;
|
|
611
|
+
stop: number;
|
|
612
|
+
risk_reward: number;
|
|
613
|
+
risk: number;
|
|
614
|
+
symbol: string;
|
|
615
|
+
profit_percent: number;
|
|
616
|
+
};
|
|
617
|
+
rr?: number;
|
|
618
|
+
global_config: GlobalConfig;
|
|
619
|
+
}): {
|
|
620
|
+
start_risk: number;
|
|
621
|
+
short_profit: number;
|
|
622
|
+
support: number;
|
|
623
|
+
resistance: number;
|
|
624
|
+
long_v: any;
|
|
625
|
+
profit_percent: number;
|
|
626
|
+
result: any;
|
|
627
|
+
short_max_size: any;
|
|
628
|
+
};
|
|
602
629
|
export type StrategyPosition = {
|
|
603
630
|
entry: number;
|
|
604
631
|
quantity: number;
|
package/dist/frontend-index.js
CHANGED
|
@@ -2132,6 +2132,52 @@ function getOptimumHedgeFactor(payload) {
|
|
|
2132
2132
|
converged: best_diff <= tolerance
|
|
2133
2133
|
};
|
|
2134
2134
|
}
|
|
2135
|
+
function determineCompoundLongTrade(payload) {
|
|
2136
|
+
const {
|
|
2137
|
+
focus_short_position,
|
|
2138
|
+
focus_long_position,
|
|
2139
|
+
shortConfig,
|
|
2140
|
+
global_config,
|
|
2141
|
+
rr = 1
|
|
2142
|
+
} = payload;
|
|
2143
|
+
const short_app_config = buildAppConfig(global_config, {
|
|
2144
|
+
entry: shortConfig.entry,
|
|
2145
|
+
stop: shortConfig.stop,
|
|
2146
|
+
risk_reward: shortConfig.risk_reward,
|
|
2147
|
+
risk: shortConfig.risk,
|
|
2148
|
+
symbol: shortConfig.symbol
|
|
2149
|
+
});
|
|
2150
|
+
const short_max_size = short_app_config.last_value.avg_size;
|
|
2151
|
+
const start_risk = Math.abs(short_app_config.last_value.neg_pnl) * rr;
|
|
2152
|
+
const short_profit = short_app_config.last_value.avg_size * short_app_config.last_value.avg_entry * shortConfig.profit_percent / 100;
|
|
2153
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - short_profit) * rr;
|
|
2154
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
2155
|
+
const result = getRiskReward({
|
|
2156
|
+
entry: resistance,
|
|
2157
|
+
stop: support,
|
|
2158
|
+
risk: start_risk,
|
|
2159
|
+
global_config,
|
|
2160
|
+
force_exact_risk: true
|
|
2161
|
+
});
|
|
2162
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
2163
|
+
entry: resistance,
|
|
2164
|
+
stop: support,
|
|
2165
|
+
risk_reward: result.risk_reward,
|
|
2166
|
+
risk: result.risk,
|
|
2167
|
+
symbol: shortConfig.symbol
|
|
2168
|
+
});
|
|
2169
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
2170
|
+
return {
|
|
2171
|
+
start_risk,
|
|
2172
|
+
short_profit,
|
|
2173
|
+
support: to_f(support, global_config.price_places),
|
|
2174
|
+
resistance: to_f(resistance, global_config.price_places),
|
|
2175
|
+
long_v: long_app_config.last_value,
|
|
2176
|
+
profit_percent: to_f(long_profit_percent, "%.3f"),
|
|
2177
|
+
result,
|
|
2178
|
+
short_max_size
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2135
2181
|
// src/helpers/strategy.ts
|
|
2136
2182
|
class Strategy {
|
|
2137
2183
|
position;
|
|
@@ -2665,6 +2711,7 @@ export {
|
|
|
2665
2711
|
determineRewardFactor,
|
|
2666
2712
|
determineOptimumRisk,
|
|
2667
2713
|
determineOptimumReward,
|
|
2714
|
+
determineCompoundLongTrade,
|
|
2668
2715
|
createGapPairs,
|
|
2669
2716
|
createArray,
|
|
2670
2717
|
computeTotalAverageForEachTrade,
|
package/dist/index.cjs
CHANGED
|
@@ -41963,6 +41963,7 @@ __export(exports_src, {
|
|
|
41963
41963
|
determineRewardFactor: () => determineRewardFactor,
|
|
41964
41964
|
determineOptimumRisk: () => determineOptimumRisk,
|
|
41965
41965
|
determineOptimumReward: () => determineOptimumReward,
|
|
41966
|
+
determineCompoundLongTrade: () => determineCompoundLongTrade,
|
|
41966
41967
|
database: () => exports_database,
|
|
41967
41968
|
createArray: () => createArray,
|
|
41968
41969
|
computeRiskReward: () => computeRiskReward,
|
|
@@ -52484,6 +52485,37 @@ class AppDatabase {
|
|
|
52484
52485
|
});
|
|
52485
52486
|
return result;
|
|
52486
52487
|
}
|
|
52488
|
+
async getPositionWithLowestNextOrder(options) {
|
|
52489
|
+
const { symbol, kind, asset, exclude } = options;
|
|
52490
|
+
try {
|
|
52491
|
+
let filter = `kind="${kind}" && next_order != null`;
|
|
52492
|
+
if (asset) {
|
|
52493
|
+
filter += ` && symbol ~ "${asset.toUpperCase()}%"`;
|
|
52494
|
+
} else if (symbol) {
|
|
52495
|
+
filter += ` && symbol:lower="${symbol.toLowerCase()}"`;
|
|
52496
|
+
} else {
|
|
52497
|
+
throw new Error("Either symbol or asset must be provided");
|
|
52498
|
+
}
|
|
52499
|
+
if (exclude && exclude.length > 0) {
|
|
52500
|
+
const excludeConditions = exclude.map((acc) => `(p_account.owner:lower != "${acc.owner.toLowerCase()}" || p_account.exchange:lower != "${acc.exchange.toLowerCase()}")`).join(" && ");
|
|
52501
|
+
filter += ` && (${excludeConditions})`;
|
|
52502
|
+
}
|
|
52503
|
+
const sortDirection = kind === "short" ? "+" : "-";
|
|
52504
|
+
const positions = await this.pb.collection("positions_view").getList(1, 1, {
|
|
52505
|
+
filter,
|
|
52506
|
+
sort: `${sortDirection}next_order`,
|
|
52507
|
+
expand: "p_account"
|
|
52508
|
+
});
|
|
52509
|
+
if (positions.items.length > 0) {
|
|
52510
|
+
return positions.items[0];
|
|
52511
|
+
}
|
|
52512
|
+
return null;
|
|
52513
|
+
} catch (error) {
|
|
52514
|
+
const identifier = asset ? `asset ${asset}` : `symbol ${symbol}`;
|
|
52515
|
+
console.error(`Error fetching position with ${kind === "short" ? "lowest" : "highest"} next_order for ${identifier} ${kind}:`, error);
|
|
52516
|
+
return null;
|
|
52517
|
+
}
|
|
52518
|
+
}
|
|
52487
52519
|
async hasExistingOrders(symbol) {
|
|
52488
52520
|
const result = await this.pb.collection("orders").getFullList({
|
|
52489
52521
|
filter: `symbol:lower="${symbol.toLowerCase()}"`
|
|
@@ -54437,6 +54469,52 @@ function getOptimumHedgeFactor(payload) {
|
|
|
54437
54469
|
converged: best_diff <= tolerance
|
|
54438
54470
|
};
|
|
54439
54471
|
}
|
|
54472
|
+
function determineCompoundLongTrade(payload) {
|
|
54473
|
+
const {
|
|
54474
|
+
focus_short_position,
|
|
54475
|
+
focus_long_position,
|
|
54476
|
+
shortConfig,
|
|
54477
|
+
global_config,
|
|
54478
|
+
rr = 1
|
|
54479
|
+
} = payload;
|
|
54480
|
+
const short_app_config = buildAppConfig(global_config, {
|
|
54481
|
+
entry: shortConfig.entry,
|
|
54482
|
+
stop: shortConfig.stop,
|
|
54483
|
+
risk_reward: shortConfig.risk_reward,
|
|
54484
|
+
risk: shortConfig.risk,
|
|
54485
|
+
symbol: shortConfig.symbol
|
|
54486
|
+
});
|
|
54487
|
+
const short_max_size = short_app_config.last_value.avg_size;
|
|
54488
|
+
const start_risk = Math.abs(short_app_config.last_value.neg_pnl) * rr;
|
|
54489
|
+
const short_profit = short_app_config.last_value.avg_size * short_app_config.last_value.avg_entry * shortConfig.profit_percent / 100;
|
|
54490
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - short_profit) * rr;
|
|
54491
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
54492
|
+
const result = getRiskReward({
|
|
54493
|
+
entry: resistance,
|
|
54494
|
+
stop: support,
|
|
54495
|
+
risk: start_risk,
|
|
54496
|
+
global_config,
|
|
54497
|
+
force_exact_risk: true
|
|
54498
|
+
});
|
|
54499
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
54500
|
+
entry: resistance,
|
|
54501
|
+
stop: support,
|
|
54502
|
+
risk_reward: result.risk_reward,
|
|
54503
|
+
risk: result.risk,
|
|
54504
|
+
symbol: shortConfig.symbol
|
|
54505
|
+
});
|
|
54506
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
54507
|
+
return {
|
|
54508
|
+
start_risk,
|
|
54509
|
+
short_profit,
|
|
54510
|
+
support: to_f2(support, global_config.price_places),
|
|
54511
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
54512
|
+
long_v: long_app_config.last_value,
|
|
54513
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
54514
|
+
result,
|
|
54515
|
+
short_max_size
|
|
54516
|
+
};
|
|
54517
|
+
}
|
|
54440
54518
|
|
|
54441
54519
|
// src/helpers/strategy.ts
|
|
54442
54520
|
class Strategy {
|
|
@@ -58520,6 +58598,10 @@ class ExchangeAccount {
|
|
|
58520
58598
|
symbol,
|
|
58521
58599
|
kind
|
|
58522
58600
|
});
|
|
58601
|
+
await this.updateRiskOnEmpty({
|
|
58602
|
+
symbol,
|
|
58603
|
+
kind
|
|
58604
|
+
});
|
|
58523
58605
|
}
|
|
58524
58606
|
const long_config = await this.getPositionConfig({
|
|
58525
58607
|
symbol,
|
|
@@ -59011,17 +59093,53 @@ class ExchangeAccount {
|
|
|
59011
59093
|
if (position2?.expand?.config) {
|
|
59012
59094
|
const config2 = position2.expand.config;
|
|
59013
59095
|
let _profit = config2.profit;
|
|
59096
|
+
let risk = config2.risk;
|
|
59097
|
+
let next_risk = position2.next_risk;
|
|
59014
59098
|
let _profit_percent = config2?.profit_percent;
|
|
59015
59099
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
59016
59100
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
59101
|
+
next_risk = risk + _profit;
|
|
59017
59102
|
}
|
|
59018
59103
|
await this.app_db.update_db_position(position2, {
|
|
59019
|
-
target_pnl: _profit
|
|
59104
|
+
target_pnl: _profit,
|
|
59105
|
+
next_risk
|
|
59020
59106
|
});
|
|
59021
59107
|
return _profit;
|
|
59022
59108
|
}
|
|
59023
59109
|
return 0;
|
|
59024
59110
|
}
|
|
59111
|
+
async updateRiskOnEmpty(payload) {
|
|
59112
|
+
const { symbol, kind } = payload;
|
|
59113
|
+
const position2 = await this.syncAccount({
|
|
59114
|
+
symbol,
|
|
59115
|
+
kind
|
|
59116
|
+
});
|
|
59117
|
+
if (position2 && position2.quantity === 0) {
|
|
59118
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
59119
|
+
await this.getPositionConfig({
|
|
59120
|
+
symbol,
|
|
59121
|
+
kind,
|
|
59122
|
+
params: {
|
|
59123
|
+
risk: position2.next_risk
|
|
59124
|
+
}
|
|
59125
|
+
});
|
|
59126
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
59127
|
+
return {
|
|
59128
|
+
updated: true,
|
|
59129
|
+
symbol,
|
|
59130
|
+
kind,
|
|
59131
|
+
old_risk: position2.expand?.config?.risk,
|
|
59132
|
+
new_risk: position2.next_risk
|
|
59133
|
+
};
|
|
59134
|
+
}
|
|
59135
|
+
}
|
|
59136
|
+
return {
|
|
59137
|
+
updated: false,
|
|
59138
|
+
symbol,
|
|
59139
|
+
kind,
|
|
59140
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
59141
|
+
};
|
|
59142
|
+
}
|
|
59025
59143
|
async updateGoodHedgeConfig(payload) {
|
|
59026
59144
|
const {
|
|
59027
59145
|
params,
|
|
@@ -60077,6 +60195,38 @@ class ExchangeAccount {
|
|
|
60077
60195
|
decimal_places: symbol_config.decimal_places
|
|
60078
60196
|
});
|
|
60079
60197
|
}
|
|
60198
|
+
async placeCompoundShortTrade(payload) {
|
|
60199
|
+
}
|
|
60200
|
+
async placeCompoundLongTrade(payload) {
|
|
60201
|
+
const { symbol, params, place = false } = payload;
|
|
60202
|
+
if (place) {
|
|
60203
|
+
await this.getPositionConfig({
|
|
60204
|
+
symbol,
|
|
60205
|
+
kind: "long",
|
|
60206
|
+
params: {
|
|
60207
|
+
entry: params.resistance,
|
|
60208
|
+
stop: params.support,
|
|
60209
|
+
risk_reward: params.risk_reward,
|
|
60210
|
+
risk: params.risk,
|
|
60211
|
+
profit_percent: params.profit_percent
|
|
60212
|
+
}
|
|
60213
|
+
});
|
|
60214
|
+
const short_position = await this.syncAccount({
|
|
60215
|
+
symbol,
|
|
60216
|
+
kind: "short",
|
|
60217
|
+
as_view: true
|
|
60218
|
+
});
|
|
60219
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
60220
|
+
await this.placeTrade({
|
|
60221
|
+
symbol,
|
|
60222
|
+
kind: "long",
|
|
60223
|
+
ignore_config: true,
|
|
60224
|
+
limit: true,
|
|
60225
|
+
place: true
|
|
60226
|
+
});
|
|
60227
|
+
}
|
|
60228
|
+
}
|
|
60229
|
+
}
|
|
60080
60230
|
}
|
|
60081
60231
|
function getExchangeKlass(exchange) {
|
|
60082
60232
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -60417,6 +60567,43 @@ class App {
|
|
|
60417
60567
|
});
|
|
60418
60568
|
return result;
|
|
60419
60569
|
}
|
|
60570
|
+
async compoundLongTrade(payload) {
|
|
60571
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
60572
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
60573
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
60574
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
60575
|
+
symbol: focus_account.symbol,
|
|
60576
|
+
as_view: true
|
|
60577
|
+
});
|
|
60578
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
60579
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
60580
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
60581
|
+
symbol: main_account.symbol,
|
|
60582
|
+
kind: "short"
|
|
60583
|
+
});
|
|
60584
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
60585
|
+
symbol: focus_account.symbol
|
|
60586
|
+
});
|
|
60587
|
+
const result = determineCompoundLongTrade({
|
|
60588
|
+
focus_short_position,
|
|
60589
|
+
focus_long_position,
|
|
60590
|
+
shortConfig,
|
|
60591
|
+
global_config: symbol_config,
|
|
60592
|
+
rr
|
|
60593
|
+
});
|
|
60594
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
60595
|
+
symbol: main_account.symbol,
|
|
60596
|
+
params: {
|
|
60597
|
+
resistance: result.resistance,
|
|
60598
|
+
support: result.support,
|
|
60599
|
+
profit_percent: result.profit_percent,
|
|
60600
|
+
risk_reward: result.result.risk_reward,
|
|
60601
|
+
risk: result.result.risk
|
|
60602
|
+
},
|
|
60603
|
+
place
|
|
60604
|
+
});
|
|
60605
|
+
return result;
|
|
60606
|
+
}
|
|
60420
60607
|
async reduceExistingPosition(payload) {
|
|
60421
60608
|
const {
|
|
60422
60609
|
main_account,
|
package/dist/index.d.ts
CHANGED
|
@@ -128,6 +128,7 @@ export interface PositionsView {
|
|
|
128
128
|
price: number;
|
|
129
129
|
quantity: number;
|
|
130
130
|
};
|
|
131
|
+
next_risk?: number;
|
|
131
132
|
}
|
|
132
133
|
export interface BullishMarket extends RecordModel {
|
|
133
134
|
id: string;
|
|
@@ -777,6 +778,12 @@ export declare class AppDatabase {
|
|
|
777
778
|
} | SymbolConfig[]>;
|
|
778
779
|
unwindSymbolFromDB(symbol: string): Promise<boolean>;
|
|
779
780
|
hasExistingPosition(symbol: string): Promise<import("pocketbase").RecordModel[]>;
|
|
781
|
+
getPositionWithLowestNextOrder(options: {
|
|
782
|
+
symbol?: string;
|
|
783
|
+
kind: "long" | "short";
|
|
784
|
+
asset?: string;
|
|
785
|
+
exclude?: ExchangeType[];
|
|
786
|
+
}): Promise<PositionsView | null>;
|
|
780
787
|
hasExistingOrders(symbol: string): Promise<import("pocketbase").RecordModel[]>;
|
|
781
788
|
removeSymbolFromUnwindingMarkets(symbol: string): Promise<boolean>;
|
|
782
789
|
removePosition(position: any): Promise<void>;
|
|
@@ -1528,6 +1535,33 @@ export declare function getOptimumHedgeFactor(payload: {
|
|
|
1528
1535
|
iterations: number;
|
|
1529
1536
|
converged: boolean;
|
|
1530
1537
|
};
|
|
1538
|
+
export type CType = {
|
|
1539
|
+
next_order: number;
|
|
1540
|
+
take_profit: number;
|
|
1541
|
+
};
|
|
1542
|
+
export declare function determineCompoundLongTrade(payload: {
|
|
1543
|
+
focus_short_position: CType;
|
|
1544
|
+
focus_long_position: CType;
|
|
1545
|
+
shortConfig: {
|
|
1546
|
+
entry: number;
|
|
1547
|
+
stop: number;
|
|
1548
|
+
risk_reward: number;
|
|
1549
|
+
risk: number;
|
|
1550
|
+
symbol: string;
|
|
1551
|
+
profit_percent: number;
|
|
1552
|
+
};
|
|
1553
|
+
rr?: number;
|
|
1554
|
+
global_config: GlobalConfig;
|
|
1555
|
+
}): {
|
|
1556
|
+
start_risk: number;
|
|
1557
|
+
short_profit: number;
|
|
1558
|
+
support: number;
|
|
1559
|
+
resistance: number;
|
|
1560
|
+
long_v: any;
|
|
1561
|
+
profit_percent: number;
|
|
1562
|
+
result: any;
|
|
1563
|
+
short_max_size: any;
|
|
1564
|
+
};
|
|
1531
1565
|
declare class ExchangePosition {
|
|
1532
1566
|
exchange: BaseExchange;
|
|
1533
1567
|
exchange_account: ExchangeAccount$1;
|
|
@@ -2015,6 +2049,32 @@ declare class ExchangeAccount$1 {
|
|
|
2015
2049
|
symbol: string;
|
|
2016
2050
|
kind: "long" | "short";
|
|
2017
2051
|
}): Promise<number>;
|
|
2052
|
+
/**
|
|
2053
|
+
* Updates the risk configuration for an empty position using the next_risk value.
|
|
2054
|
+
* This implements progressive risk management where successful trades increase future risk tolerance.
|
|
2055
|
+
*
|
|
2056
|
+
* @param payload.symbol - The trading symbol (e.g., "BTCUSDT")
|
|
2057
|
+
* @param payload.kind - Position type: "long" or "short"
|
|
2058
|
+
* @returns Object indicating if update was successful with old/new risk values
|
|
2059
|
+
*/
|
|
2060
|
+
updateRiskOnEmpty(payload: {
|
|
2061
|
+
symbol: string;
|
|
2062
|
+
kind: "long" | "short";
|
|
2063
|
+
}): Promise<{
|
|
2064
|
+
updated: boolean;
|
|
2065
|
+
symbol: string;
|
|
2066
|
+
kind: "long" | "short";
|
|
2067
|
+
old_risk: number;
|
|
2068
|
+
new_risk: number;
|
|
2069
|
+
reason?: undefined;
|
|
2070
|
+
} | {
|
|
2071
|
+
updated: boolean;
|
|
2072
|
+
symbol: string;
|
|
2073
|
+
kind: "long" | "short";
|
|
2074
|
+
reason: string;
|
|
2075
|
+
old_risk?: undefined;
|
|
2076
|
+
new_risk?: undefined;
|
|
2077
|
+
}>;
|
|
2018
2078
|
updateGoodHedgeConfig(payload: {
|
|
2019
2079
|
symbol: string;
|
|
2020
2080
|
params?: {
|
|
@@ -2336,6 +2396,18 @@ declare class ExchangeAccount$1 {
|
|
|
2336
2396
|
price_places: string;
|
|
2337
2397
|
decimal_places: string;
|
|
2338
2398
|
}>;
|
|
2399
|
+
placeCompoundShortTrade(payload: {}): Promise<void>;
|
|
2400
|
+
placeCompoundLongTrade(payload: {
|
|
2401
|
+
symbol: string;
|
|
2402
|
+
params: {
|
|
2403
|
+
resistance: number;
|
|
2404
|
+
support: number;
|
|
2405
|
+
profit_percent: number;
|
|
2406
|
+
risk_reward: number;
|
|
2407
|
+
risk: number;
|
|
2408
|
+
};
|
|
2409
|
+
place?: boolean;
|
|
2410
|
+
}): Promise<void>;
|
|
2339
2411
|
}
|
|
2340
2412
|
declare function getExchangeAccount(payload: {
|
|
2341
2413
|
account: ExchangeType;
|
|
@@ -2530,6 +2602,25 @@ declare class App {
|
|
|
2530
2602
|
pnl: number;
|
|
2531
2603
|
};
|
|
2532
2604
|
}>;
|
|
2605
|
+
compoundLongTrade(payload: {
|
|
2606
|
+
main_account: ExchangeType & {
|
|
2607
|
+
symbol: string;
|
|
2608
|
+
};
|
|
2609
|
+
focus_account: ExchangeType & {
|
|
2610
|
+
symbol: string;
|
|
2611
|
+
};
|
|
2612
|
+
place?: boolean;
|
|
2613
|
+
rr?: number;
|
|
2614
|
+
}): Promise<{
|
|
2615
|
+
start_risk: number;
|
|
2616
|
+
short_profit: number;
|
|
2617
|
+
support: number;
|
|
2618
|
+
resistance: number;
|
|
2619
|
+
long_v: any;
|
|
2620
|
+
profit_percent: number;
|
|
2621
|
+
result: any;
|
|
2622
|
+
short_max_size: any;
|
|
2623
|
+
}>;
|
|
2533
2624
|
reduceExistingPosition(payload: {
|
|
2534
2625
|
main_account: ExchangeType & {
|
|
2535
2626
|
symbol: string;
|
package/dist/index.js
CHANGED
|
@@ -52433,6 +52433,37 @@ class AppDatabase {
|
|
|
52433
52433
|
});
|
|
52434
52434
|
return result;
|
|
52435
52435
|
}
|
|
52436
|
+
async getPositionWithLowestNextOrder(options) {
|
|
52437
|
+
const { symbol, kind, asset, exclude } = options;
|
|
52438
|
+
try {
|
|
52439
|
+
let filter = `kind="${kind}" && next_order != null`;
|
|
52440
|
+
if (asset) {
|
|
52441
|
+
filter += ` && symbol ~ "${asset.toUpperCase()}%"`;
|
|
52442
|
+
} else if (symbol) {
|
|
52443
|
+
filter += ` && symbol:lower="${symbol.toLowerCase()}"`;
|
|
52444
|
+
} else {
|
|
52445
|
+
throw new Error("Either symbol or asset must be provided");
|
|
52446
|
+
}
|
|
52447
|
+
if (exclude && exclude.length > 0) {
|
|
52448
|
+
const excludeConditions = exclude.map((acc) => `(p_account.owner:lower != "${acc.owner.toLowerCase()}" || p_account.exchange:lower != "${acc.exchange.toLowerCase()}")`).join(" && ");
|
|
52449
|
+
filter += ` && (${excludeConditions})`;
|
|
52450
|
+
}
|
|
52451
|
+
const sortDirection = kind === "short" ? "+" : "-";
|
|
52452
|
+
const positions = await this.pb.collection("positions_view").getList(1, 1, {
|
|
52453
|
+
filter,
|
|
52454
|
+
sort: `${sortDirection}next_order`,
|
|
52455
|
+
expand: "p_account"
|
|
52456
|
+
});
|
|
52457
|
+
if (positions.items.length > 0) {
|
|
52458
|
+
return positions.items[0];
|
|
52459
|
+
}
|
|
52460
|
+
return null;
|
|
52461
|
+
} catch (error) {
|
|
52462
|
+
const identifier = asset ? `asset ${asset}` : `symbol ${symbol}`;
|
|
52463
|
+
console.error(`Error fetching position with ${kind === "short" ? "lowest" : "highest"} next_order for ${identifier} ${kind}:`, error);
|
|
52464
|
+
return null;
|
|
52465
|
+
}
|
|
52466
|
+
}
|
|
52436
52467
|
async hasExistingOrders(symbol) {
|
|
52437
52468
|
const result = await this.pb.collection("orders").getFullList({
|
|
52438
52469
|
filter: `symbol:lower="${symbol.toLowerCase()}"`
|
|
@@ -54386,6 +54417,52 @@ function getOptimumHedgeFactor(payload) {
|
|
|
54386
54417
|
converged: best_diff <= tolerance
|
|
54387
54418
|
};
|
|
54388
54419
|
}
|
|
54420
|
+
function determineCompoundLongTrade(payload) {
|
|
54421
|
+
const {
|
|
54422
|
+
focus_short_position,
|
|
54423
|
+
focus_long_position,
|
|
54424
|
+
shortConfig,
|
|
54425
|
+
global_config,
|
|
54426
|
+
rr = 1
|
|
54427
|
+
} = payload;
|
|
54428
|
+
const short_app_config = buildAppConfig(global_config, {
|
|
54429
|
+
entry: shortConfig.entry,
|
|
54430
|
+
stop: shortConfig.stop,
|
|
54431
|
+
risk_reward: shortConfig.risk_reward,
|
|
54432
|
+
risk: shortConfig.risk,
|
|
54433
|
+
symbol: shortConfig.symbol
|
|
54434
|
+
});
|
|
54435
|
+
const short_max_size = short_app_config.last_value.avg_size;
|
|
54436
|
+
const start_risk = Math.abs(short_app_config.last_value.neg_pnl) * rr;
|
|
54437
|
+
const short_profit = short_app_config.last_value.avg_size * short_app_config.last_value.avg_entry * shortConfig.profit_percent / 100;
|
|
54438
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - short_profit) * rr;
|
|
54439
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
54440
|
+
const result = getRiskReward({
|
|
54441
|
+
entry: resistance,
|
|
54442
|
+
stop: support,
|
|
54443
|
+
risk: start_risk,
|
|
54444
|
+
global_config,
|
|
54445
|
+
force_exact_risk: true
|
|
54446
|
+
});
|
|
54447
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
54448
|
+
entry: resistance,
|
|
54449
|
+
stop: support,
|
|
54450
|
+
risk_reward: result.risk_reward,
|
|
54451
|
+
risk: result.risk,
|
|
54452
|
+
symbol: shortConfig.symbol
|
|
54453
|
+
});
|
|
54454
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
54455
|
+
return {
|
|
54456
|
+
start_risk,
|
|
54457
|
+
short_profit,
|
|
54458
|
+
support: to_f2(support, global_config.price_places),
|
|
54459
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
54460
|
+
long_v: long_app_config.last_value,
|
|
54461
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
54462
|
+
result,
|
|
54463
|
+
short_max_size
|
|
54464
|
+
};
|
|
54465
|
+
}
|
|
54389
54466
|
|
|
54390
54467
|
// src/helpers/strategy.ts
|
|
54391
54468
|
class Strategy {
|
|
@@ -58469,6 +58546,10 @@ class ExchangeAccount {
|
|
|
58469
58546
|
symbol,
|
|
58470
58547
|
kind
|
|
58471
58548
|
});
|
|
58549
|
+
await this.updateRiskOnEmpty({
|
|
58550
|
+
symbol,
|
|
58551
|
+
kind
|
|
58552
|
+
});
|
|
58472
58553
|
}
|
|
58473
58554
|
const long_config = await this.getPositionConfig({
|
|
58474
58555
|
symbol,
|
|
@@ -58960,17 +59041,53 @@ class ExchangeAccount {
|
|
|
58960
59041
|
if (position2?.expand?.config) {
|
|
58961
59042
|
const config2 = position2.expand.config;
|
|
58962
59043
|
let _profit = config2.profit;
|
|
59044
|
+
let risk = config2.risk;
|
|
59045
|
+
let next_risk = position2.next_risk;
|
|
58963
59046
|
let _profit_percent = config2?.profit_percent;
|
|
58964
59047
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
58965
59048
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
59049
|
+
next_risk = risk + _profit;
|
|
58966
59050
|
}
|
|
58967
59051
|
await this.app_db.update_db_position(position2, {
|
|
58968
|
-
target_pnl: _profit
|
|
59052
|
+
target_pnl: _profit,
|
|
59053
|
+
next_risk
|
|
58969
59054
|
});
|
|
58970
59055
|
return _profit;
|
|
58971
59056
|
}
|
|
58972
59057
|
return 0;
|
|
58973
59058
|
}
|
|
59059
|
+
async updateRiskOnEmpty(payload) {
|
|
59060
|
+
const { symbol, kind } = payload;
|
|
59061
|
+
const position2 = await this.syncAccount({
|
|
59062
|
+
symbol,
|
|
59063
|
+
kind
|
|
59064
|
+
});
|
|
59065
|
+
if (position2 && position2.quantity === 0) {
|
|
59066
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
59067
|
+
await this.getPositionConfig({
|
|
59068
|
+
symbol,
|
|
59069
|
+
kind,
|
|
59070
|
+
params: {
|
|
59071
|
+
risk: position2.next_risk
|
|
59072
|
+
}
|
|
59073
|
+
});
|
|
59074
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
59075
|
+
return {
|
|
59076
|
+
updated: true,
|
|
59077
|
+
symbol,
|
|
59078
|
+
kind,
|
|
59079
|
+
old_risk: position2.expand?.config?.risk,
|
|
59080
|
+
new_risk: position2.next_risk
|
|
59081
|
+
};
|
|
59082
|
+
}
|
|
59083
|
+
}
|
|
59084
|
+
return {
|
|
59085
|
+
updated: false,
|
|
59086
|
+
symbol,
|
|
59087
|
+
kind,
|
|
59088
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
59089
|
+
};
|
|
59090
|
+
}
|
|
58974
59091
|
async updateGoodHedgeConfig(payload) {
|
|
58975
59092
|
const {
|
|
58976
59093
|
params,
|
|
@@ -60026,6 +60143,38 @@ class ExchangeAccount {
|
|
|
60026
60143
|
decimal_places: symbol_config.decimal_places
|
|
60027
60144
|
});
|
|
60028
60145
|
}
|
|
60146
|
+
async placeCompoundShortTrade(payload) {
|
|
60147
|
+
}
|
|
60148
|
+
async placeCompoundLongTrade(payload) {
|
|
60149
|
+
const { symbol, params, place = false } = payload;
|
|
60150
|
+
if (place) {
|
|
60151
|
+
await this.getPositionConfig({
|
|
60152
|
+
symbol,
|
|
60153
|
+
kind: "long",
|
|
60154
|
+
params: {
|
|
60155
|
+
entry: params.resistance,
|
|
60156
|
+
stop: params.support,
|
|
60157
|
+
risk_reward: params.risk_reward,
|
|
60158
|
+
risk: params.risk,
|
|
60159
|
+
profit_percent: params.profit_percent
|
|
60160
|
+
}
|
|
60161
|
+
});
|
|
60162
|
+
const short_position = await this.syncAccount({
|
|
60163
|
+
symbol,
|
|
60164
|
+
kind: "short",
|
|
60165
|
+
as_view: true
|
|
60166
|
+
});
|
|
60167
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
60168
|
+
await this.placeTrade({
|
|
60169
|
+
symbol,
|
|
60170
|
+
kind: "long",
|
|
60171
|
+
ignore_config: true,
|
|
60172
|
+
limit: true,
|
|
60173
|
+
place: true
|
|
60174
|
+
});
|
|
60175
|
+
}
|
|
60176
|
+
}
|
|
60177
|
+
}
|
|
60029
60178
|
}
|
|
60030
60179
|
function getExchangeKlass(exchange) {
|
|
60031
60180
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -60366,6 +60515,43 @@ class App {
|
|
|
60366
60515
|
});
|
|
60367
60516
|
return result;
|
|
60368
60517
|
}
|
|
60518
|
+
async compoundLongTrade(payload) {
|
|
60519
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
60520
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
60521
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
60522
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
60523
|
+
symbol: focus_account.symbol,
|
|
60524
|
+
as_view: true
|
|
60525
|
+
});
|
|
60526
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
60527
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
60528
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
60529
|
+
symbol: main_account.symbol,
|
|
60530
|
+
kind: "short"
|
|
60531
|
+
});
|
|
60532
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
60533
|
+
symbol: focus_account.symbol
|
|
60534
|
+
});
|
|
60535
|
+
const result = determineCompoundLongTrade({
|
|
60536
|
+
focus_short_position,
|
|
60537
|
+
focus_long_position,
|
|
60538
|
+
shortConfig,
|
|
60539
|
+
global_config: symbol_config,
|
|
60540
|
+
rr
|
|
60541
|
+
});
|
|
60542
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
60543
|
+
symbol: main_account.symbol,
|
|
60544
|
+
params: {
|
|
60545
|
+
resistance: result.resistance,
|
|
60546
|
+
support: result.support,
|
|
60547
|
+
profit_percent: result.profit_percent,
|
|
60548
|
+
risk_reward: result.result.risk_reward,
|
|
60549
|
+
risk: result.result.risk
|
|
60550
|
+
},
|
|
60551
|
+
place
|
|
60552
|
+
});
|
|
60553
|
+
return result;
|
|
60554
|
+
}
|
|
60369
60555
|
async reduceExistingPosition(payload) {
|
|
60370
60556
|
const {
|
|
60371
60557
|
main_account,
|
|
@@ -60507,6 +60693,7 @@ export {
|
|
|
60507
60693
|
determineRewardFactor,
|
|
60508
60694
|
determineOptimumRisk,
|
|
60509
60695
|
determineOptimumReward,
|
|
60696
|
+
determineCompoundLongTrade,
|
|
60510
60697
|
exports_database as database,
|
|
60511
60698
|
createArray,
|
|
60512
60699
|
computeRiskReward,
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -59187,6 +59187,37 @@ class AppDatabase {
|
|
|
59187
59187
|
});
|
|
59188
59188
|
return result;
|
|
59189
59189
|
}
|
|
59190
|
+
async getPositionWithLowestNextOrder(options) {
|
|
59191
|
+
const { symbol, kind, asset, exclude } = options;
|
|
59192
|
+
try {
|
|
59193
|
+
let filter = `kind="${kind}" && next_order != null`;
|
|
59194
|
+
if (asset) {
|
|
59195
|
+
filter += ` && symbol ~ "${asset.toUpperCase()}%"`;
|
|
59196
|
+
} else if (symbol) {
|
|
59197
|
+
filter += ` && symbol:lower="${symbol.toLowerCase()}"`;
|
|
59198
|
+
} else {
|
|
59199
|
+
throw new Error("Either symbol or asset must be provided");
|
|
59200
|
+
}
|
|
59201
|
+
if (exclude && exclude.length > 0) {
|
|
59202
|
+
const excludeConditions = exclude.map((acc) => `(p_account.owner:lower != "${acc.owner.toLowerCase()}" || p_account.exchange:lower != "${acc.exchange.toLowerCase()}")`).join(" && ");
|
|
59203
|
+
filter += ` && (${excludeConditions})`;
|
|
59204
|
+
}
|
|
59205
|
+
const sortDirection = kind === "short" ? "+" : "-";
|
|
59206
|
+
const positions = await this.pb.collection("positions_view").getList(1, 1, {
|
|
59207
|
+
filter,
|
|
59208
|
+
sort: `${sortDirection}next_order`,
|
|
59209
|
+
expand: "p_account"
|
|
59210
|
+
});
|
|
59211
|
+
if (positions.items.length > 0) {
|
|
59212
|
+
return positions.items[0];
|
|
59213
|
+
}
|
|
59214
|
+
return null;
|
|
59215
|
+
} catch (error) {
|
|
59216
|
+
const identifier = asset ? `asset ${asset}` : `symbol ${symbol}`;
|
|
59217
|
+
console.error(`Error fetching position with ${kind === "short" ? "lowest" : "highest"} next_order for ${identifier} ${kind}:`, error);
|
|
59218
|
+
return null;
|
|
59219
|
+
}
|
|
59220
|
+
}
|
|
59190
59221
|
async hasExistingOrders(symbol) {
|
|
59191
59222
|
const result = await this.pb.collection("orders").getFullList({
|
|
59192
59223
|
filter: `symbol:lower="${symbol.toLowerCase()}"`
|
|
@@ -61114,6 +61145,52 @@ function getOptimumHedgeFactor(payload) {
|
|
|
61114
61145
|
converged: best_diff <= tolerance
|
|
61115
61146
|
};
|
|
61116
61147
|
}
|
|
61148
|
+
function determineCompoundLongTrade(payload) {
|
|
61149
|
+
const {
|
|
61150
|
+
focus_short_position,
|
|
61151
|
+
focus_long_position,
|
|
61152
|
+
shortConfig,
|
|
61153
|
+
global_config,
|
|
61154
|
+
rr = 1
|
|
61155
|
+
} = payload;
|
|
61156
|
+
const short_app_config = buildAppConfig(global_config, {
|
|
61157
|
+
entry: shortConfig.entry,
|
|
61158
|
+
stop: shortConfig.stop,
|
|
61159
|
+
risk_reward: shortConfig.risk_reward,
|
|
61160
|
+
risk: shortConfig.risk,
|
|
61161
|
+
symbol: shortConfig.symbol
|
|
61162
|
+
});
|
|
61163
|
+
const short_max_size = short_app_config.last_value.avg_size;
|
|
61164
|
+
const start_risk = Math.abs(short_app_config.last_value.neg_pnl) * rr;
|
|
61165
|
+
const short_profit = short_app_config.last_value.avg_size * short_app_config.last_value.avg_entry * shortConfig.profit_percent / 100;
|
|
61166
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - short_profit) * rr;
|
|
61167
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
61168
|
+
const result = getRiskReward({
|
|
61169
|
+
entry: resistance,
|
|
61170
|
+
stop: support,
|
|
61171
|
+
risk: start_risk,
|
|
61172
|
+
global_config,
|
|
61173
|
+
force_exact_risk: true
|
|
61174
|
+
});
|
|
61175
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
61176
|
+
entry: resistance,
|
|
61177
|
+
stop: support,
|
|
61178
|
+
risk_reward: result.risk_reward,
|
|
61179
|
+
risk: result.risk,
|
|
61180
|
+
symbol: shortConfig.symbol
|
|
61181
|
+
});
|
|
61182
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
61183
|
+
return {
|
|
61184
|
+
start_risk,
|
|
61185
|
+
short_profit,
|
|
61186
|
+
support: to_f2(support, global_config.price_places),
|
|
61187
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
61188
|
+
long_v: long_app_config.last_value,
|
|
61189
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
61190
|
+
result,
|
|
61191
|
+
short_max_size
|
|
61192
|
+
};
|
|
61193
|
+
}
|
|
61117
61194
|
|
|
61118
61195
|
// src/helpers/strategy.ts
|
|
61119
61196
|
class Strategy {
|
|
@@ -65197,6 +65274,10 @@ class ExchangeAccount {
|
|
|
65197
65274
|
symbol,
|
|
65198
65275
|
kind
|
|
65199
65276
|
});
|
|
65277
|
+
await this.updateRiskOnEmpty({
|
|
65278
|
+
symbol,
|
|
65279
|
+
kind
|
|
65280
|
+
});
|
|
65200
65281
|
}
|
|
65201
65282
|
const long_config = await this.getPositionConfig({
|
|
65202
65283
|
symbol,
|
|
@@ -65688,17 +65769,53 @@ class ExchangeAccount {
|
|
|
65688
65769
|
if (position2?.expand?.config) {
|
|
65689
65770
|
const config2 = position2.expand.config;
|
|
65690
65771
|
let _profit = config2.profit;
|
|
65772
|
+
let risk = config2.risk;
|
|
65773
|
+
let next_risk = position2.next_risk;
|
|
65691
65774
|
let _profit_percent = config2?.profit_percent;
|
|
65692
65775
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
65693
65776
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
65777
|
+
next_risk = risk + _profit;
|
|
65694
65778
|
}
|
|
65695
65779
|
await this.app_db.update_db_position(position2, {
|
|
65696
|
-
target_pnl: _profit
|
|
65780
|
+
target_pnl: _profit,
|
|
65781
|
+
next_risk
|
|
65697
65782
|
});
|
|
65698
65783
|
return _profit;
|
|
65699
65784
|
}
|
|
65700
65785
|
return 0;
|
|
65701
65786
|
}
|
|
65787
|
+
async updateRiskOnEmpty(payload) {
|
|
65788
|
+
const { symbol, kind } = payload;
|
|
65789
|
+
const position2 = await this.syncAccount({
|
|
65790
|
+
symbol,
|
|
65791
|
+
kind
|
|
65792
|
+
});
|
|
65793
|
+
if (position2 && position2.quantity === 0) {
|
|
65794
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
65795
|
+
await this.getPositionConfig({
|
|
65796
|
+
symbol,
|
|
65797
|
+
kind,
|
|
65798
|
+
params: {
|
|
65799
|
+
risk: position2.next_risk
|
|
65800
|
+
}
|
|
65801
|
+
});
|
|
65802
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
65803
|
+
return {
|
|
65804
|
+
updated: true,
|
|
65805
|
+
symbol,
|
|
65806
|
+
kind,
|
|
65807
|
+
old_risk: position2.expand?.config?.risk,
|
|
65808
|
+
new_risk: position2.next_risk
|
|
65809
|
+
};
|
|
65810
|
+
}
|
|
65811
|
+
}
|
|
65812
|
+
return {
|
|
65813
|
+
updated: false,
|
|
65814
|
+
symbol,
|
|
65815
|
+
kind,
|
|
65816
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
65817
|
+
};
|
|
65818
|
+
}
|
|
65702
65819
|
async updateGoodHedgeConfig(payload) {
|
|
65703
65820
|
const {
|
|
65704
65821
|
params,
|
|
@@ -66754,6 +66871,38 @@ class ExchangeAccount {
|
|
|
66754
66871
|
decimal_places: symbol_config.decimal_places
|
|
66755
66872
|
});
|
|
66756
66873
|
}
|
|
66874
|
+
async placeCompoundShortTrade(payload) {
|
|
66875
|
+
}
|
|
66876
|
+
async placeCompoundLongTrade(payload) {
|
|
66877
|
+
const { symbol, params, place = false } = payload;
|
|
66878
|
+
if (place) {
|
|
66879
|
+
await this.getPositionConfig({
|
|
66880
|
+
symbol,
|
|
66881
|
+
kind: "long",
|
|
66882
|
+
params: {
|
|
66883
|
+
entry: params.resistance,
|
|
66884
|
+
stop: params.support,
|
|
66885
|
+
risk_reward: params.risk_reward,
|
|
66886
|
+
risk: params.risk,
|
|
66887
|
+
profit_percent: params.profit_percent
|
|
66888
|
+
}
|
|
66889
|
+
});
|
|
66890
|
+
const short_position = await this.syncAccount({
|
|
66891
|
+
symbol,
|
|
66892
|
+
kind: "short",
|
|
66893
|
+
as_view: true
|
|
66894
|
+
});
|
|
66895
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
66896
|
+
await this.placeTrade({
|
|
66897
|
+
symbol,
|
|
66898
|
+
kind: "long",
|
|
66899
|
+
ignore_config: true,
|
|
66900
|
+
limit: true,
|
|
66901
|
+
place: true
|
|
66902
|
+
});
|
|
66903
|
+
}
|
|
66904
|
+
}
|
|
66905
|
+
}
|
|
66757
66906
|
}
|
|
66758
66907
|
function getExchangeKlass(exchange) {
|
|
66759
66908
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -67094,6 +67243,43 @@ class App {
|
|
|
67094
67243
|
});
|
|
67095
67244
|
return result;
|
|
67096
67245
|
}
|
|
67246
|
+
async compoundLongTrade(payload) {
|
|
67247
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
67248
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
67249
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
67250
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
67251
|
+
symbol: focus_account.symbol,
|
|
67252
|
+
as_view: true
|
|
67253
|
+
});
|
|
67254
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
67255
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
67256
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
67257
|
+
symbol: main_account.symbol,
|
|
67258
|
+
kind: "short"
|
|
67259
|
+
});
|
|
67260
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
67261
|
+
symbol: focus_account.symbol
|
|
67262
|
+
});
|
|
67263
|
+
const result = determineCompoundLongTrade({
|
|
67264
|
+
focus_short_position,
|
|
67265
|
+
focus_long_position,
|
|
67266
|
+
shortConfig,
|
|
67267
|
+
global_config: symbol_config,
|
|
67268
|
+
rr
|
|
67269
|
+
});
|
|
67270
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
67271
|
+
symbol: main_account.symbol,
|
|
67272
|
+
params: {
|
|
67273
|
+
resistance: result.resistance,
|
|
67274
|
+
support: result.support,
|
|
67275
|
+
profit_percent: result.profit_percent,
|
|
67276
|
+
risk_reward: result.result.risk_reward,
|
|
67277
|
+
risk: result.result.risk
|
|
67278
|
+
},
|
|
67279
|
+
place
|
|
67280
|
+
});
|
|
67281
|
+
return result;
|
|
67282
|
+
}
|
|
67097
67283
|
async reduceExistingPosition(payload) {
|
|
67098
67284
|
const {
|
|
67099
67285
|
main_account,
|
package/dist/mcp-server.js
CHANGED
|
@@ -59164,6 +59164,37 @@ class AppDatabase {
|
|
|
59164
59164
|
});
|
|
59165
59165
|
return result;
|
|
59166
59166
|
}
|
|
59167
|
+
async getPositionWithLowestNextOrder(options) {
|
|
59168
|
+
const { symbol, kind, asset, exclude } = options;
|
|
59169
|
+
try {
|
|
59170
|
+
let filter = `kind="${kind}" && next_order != null`;
|
|
59171
|
+
if (asset) {
|
|
59172
|
+
filter += ` && symbol ~ "${asset.toUpperCase()}%"`;
|
|
59173
|
+
} else if (symbol) {
|
|
59174
|
+
filter += ` && symbol:lower="${symbol.toLowerCase()}"`;
|
|
59175
|
+
} else {
|
|
59176
|
+
throw new Error("Either symbol or asset must be provided");
|
|
59177
|
+
}
|
|
59178
|
+
if (exclude && exclude.length > 0) {
|
|
59179
|
+
const excludeConditions = exclude.map((acc) => `(p_account.owner:lower != "${acc.owner.toLowerCase()}" || p_account.exchange:lower != "${acc.exchange.toLowerCase()}")`).join(" && ");
|
|
59180
|
+
filter += ` && (${excludeConditions})`;
|
|
59181
|
+
}
|
|
59182
|
+
const sortDirection = kind === "short" ? "+" : "-";
|
|
59183
|
+
const positions = await this.pb.collection("positions_view").getList(1, 1, {
|
|
59184
|
+
filter,
|
|
59185
|
+
sort: `${sortDirection}next_order`,
|
|
59186
|
+
expand: "p_account"
|
|
59187
|
+
});
|
|
59188
|
+
if (positions.items.length > 0) {
|
|
59189
|
+
return positions.items[0];
|
|
59190
|
+
}
|
|
59191
|
+
return null;
|
|
59192
|
+
} catch (error) {
|
|
59193
|
+
const identifier = asset ? `asset ${asset}` : `symbol ${symbol}`;
|
|
59194
|
+
console.error(`Error fetching position with ${kind === "short" ? "lowest" : "highest"} next_order for ${identifier} ${kind}:`, error);
|
|
59195
|
+
return null;
|
|
59196
|
+
}
|
|
59197
|
+
}
|
|
59167
59198
|
async hasExistingOrders(symbol) {
|
|
59168
59199
|
const result = await this.pb.collection("orders").getFullList({
|
|
59169
59200
|
filter: `symbol:lower="${symbol.toLowerCase()}"`
|
|
@@ -61091,6 +61122,52 @@ function getOptimumHedgeFactor(payload) {
|
|
|
61091
61122
|
converged: best_diff <= tolerance
|
|
61092
61123
|
};
|
|
61093
61124
|
}
|
|
61125
|
+
function determineCompoundLongTrade(payload) {
|
|
61126
|
+
const {
|
|
61127
|
+
focus_short_position,
|
|
61128
|
+
focus_long_position,
|
|
61129
|
+
shortConfig,
|
|
61130
|
+
global_config,
|
|
61131
|
+
rr = 1
|
|
61132
|
+
} = payload;
|
|
61133
|
+
const short_app_config = buildAppConfig(global_config, {
|
|
61134
|
+
entry: shortConfig.entry,
|
|
61135
|
+
stop: shortConfig.stop,
|
|
61136
|
+
risk_reward: shortConfig.risk_reward,
|
|
61137
|
+
risk: shortConfig.risk,
|
|
61138
|
+
symbol: shortConfig.symbol
|
|
61139
|
+
});
|
|
61140
|
+
const short_max_size = short_app_config.last_value.avg_size;
|
|
61141
|
+
const start_risk = Math.abs(short_app_config.last_value.neg_pnl) * rr;
|
|
61142
|
+
const short_profit = short_app_config.last_value.avg_size * short_app_config.last_value.avg_entry * shortConfig.profit_percent / 100;
|
|
61143
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - short_profit) * rr;
|
|
61144
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
61145
|
+
const result = getRiskReward({
|
|
61146
|
+
entry: resistance,
|
|
61147
|
+
stop: support,
|
|
61148
|
+
risk: start_risk,
|
|
61149
|
+
global_config,
|
|
61150
|
+
force_exact_risk: true
|
|
61151
|
+
});
|
|
61152
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
61153
|
+
entry: resistance,
|
|
61154
|
+
stop: support,
|
|
61155
|
+
risk_reward: result.risk_reward,
|
|
61156
|
+
risk: result.risk,
|
|
61157
|
+
symbol: shortConfig.symbol
|
|
61158
|
+
});
|
|
61159
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
61160
|
+
return {
|
|
61161
|
+
start_risk,
|
|
61162
|
+
short_profit,
|
|
61163
|
+
support: to_f2(support, global_config.price_places),
|
|
61164
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
61165
|
+
long_v: long_app_config.last_value,
|
|
61166
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
61167
|
+
result,
|
|
61168
|
+
short_max_size
|
|
61169
|
+
};
|
|
61170
|
+
}
|
|
61094
61171
|
|
|
61095
61172
|
// src/helpers/strategy.ts
|
|
61096
61173
|
class Strategy {
|
|
@@ -65174,6 +65251,10 @@ class ExchangeAccount {
|
|
|
65174
65251
|
symbol,
|
|
65175
65252
|
kind
|
|
65176
65253
|
});
|
|
65254
|
+
await this.updateRiskOnEmpty({
|
|
65255
|
+
symbol,
|
|
65256
|
+
kind
|
|
65257
|
+
});
|
|
65177
65258
|
}
|
|
65178
65259
|
const long_config = await this.getPositionConfig({
|
|
65179
65260
|
symbol,
|
|
@@ -65665,17 +65746,53 @@ class ExchangeAccount {
|
|
|
65665
65746
|
if (position2?.expand?.config) {
|
|
65666
65747
|
const config2 = position2.expand.config;
|
|
65667
65748
|
let _profit = config2.profit;
|
|
65749
|
+
let risk = config2.risk;
|
|
65750
|
+
let next_risk = position2.next_risk;
|
|
65668
65751
|
let _profit_percent = config2?.profit_percent;
|
|
65669
65752
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
65670
65753
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
65754
|
+
next_risk = risk + _profit;
|
|
65671
65755
|
}
|
|
65672
65756
|
await this.app_db.update_db_position(position2, {
|
|
65673
|
-
target_pnl: _profit
|
|
65757
|
+
target_pnl: _profit,
|
|
65758
|
+
next_risk
|
|
65674
65759
|
});
|
|
65675
65760
|
return _profit;
|
|
65676
65761
|
}
|
|
65677
65762
|
return 0;
|
|
65678
65763
|
}
|
|
65764
|
+
async updateRiskOnEmpty(payload) {
|
|
65765
|
+
const { symbol, kind } = payload;
|
|
65766
|
+
const position2 = await this.syncAccount({
|
|
65767
|
+
symbol,
|
|
65768
|
+
kind
|
|
65769
|
+
});
|
|
65770
|
+
if (position2 && position2.quantity === 0) {
|
|
65771
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
65772
|
+
await this.getPositionConfig({
|
|
65773
|
+
symbol,
|
|
65774
|
+
kind,
|
|
65775
|
+
params: {
|
|
65776
|
+
risk: position2.next_risk
|
|
65777
|
+
}
|
|
65778
|
+
});
|
|
65779
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
65780
|
+
return {
|
|
65781
|
+
updated: true,
|
|
65782
|
+
symbol,
|
|
65783
|
+
kind,
|
|
65784
|
+
old_risk: position2.expand?.config?.risk,
|
|
65785
|
+
new_risk: position2.next_risk
|
|
65786
|
+
};
|
|
65787
|
+
}
|
|
65788
|
+
}
|
|
65789
|
+
return {
|
|
65790
|
+
updated: false,
|
|
65791
|
+
symbol,
|
|
65792
|
+
kind,
|
|
65793
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
65794
|
+
};
|
|
65795
|
+
}
|
|
65679
65796
|
async updateGoodHedgeConfig(payload) {
|
|
65680
65797
|
const {
|
|
65681
65798
|
params,
|
|
@@ -66731,6 +66848,38 @@ class ExchangeAccount {
|
|
|
66731
66848
|
decimal_places: symbol_config.decimal_places
|
|
66732
66849
|
});
|
|
66733
66850
|
}
|
|
66851
|
+
async placeCompoundShortTrade(payload) {
|
|
66852
|
+
}
|
|
66853
|
+
async placeCompoundLongTrade(payload) {
|
|
66854
|
+
const { symbol, params, place = false } = payload;
|
|
66855
|
+
if (place) {
|
|
66856
|
+
await this.getPositionConfig({
|
|
66857
|
+
symbol,
|
|
66858
|
+
kind: "long",
|
|
66859
|
+
params: {
|
|
66860
|
+
entry: params.resistance,
|
|
66861
|
+
stop: params.support,
|
|
66862
|
+
risk_reward: params.risk_reward,
|
|
66863
|
+
risk: params.risk,
|
|
66864
|
+
profit_percent: params.profit_percent
|
|
66865
|
+
}
|
|
66866
|
+
});
|
|
66867
|
+
const short_position = await this.syncAccount({
|
|
66868
|
+
symbol,
|
|
66869
|
+
kind: "short",
|
|
66870
|
+
as_view: true
|
|
66871
|
+
});
|
|
66872
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
66873
|
+
await this.placeTrade({
|
|
66874
|
+
symbol,
|
|
66875
|
+
kind: "long",
|
|
66876
|
+
ignore_config: true,
|
|
66877
|
+
limit: true,
|
|
66878
|
+
place: true
|
|
66879
|
+
});
|
|
66880
|
+
}
|
|
66881
|
+
}
|
|
66882
|
+
}
|
|
66734
66883
|
}
|
|
66735
66884
|
function getExchangeKlass(exchange) {
|
|
66736
66885
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -67071,6 +67220,43 @@ class App {
|
|
|
67071
67220
|
});
|
|
67072
67221
|
return result;
|
|
67073
67222
|
}
|
|
67223
|
+
async compoundLongTrade(payload) {
|
|
67224
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
67225
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
67226
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
67227
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
67228
|
+
symbol: focus_account.symbol,
|
|
67229
|
+
as_view: true
|
|
67230
|
+
});
|
|
67231
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
67232
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
67233
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
67234
|
+
symbol: main_account.symbol,
|
|
67235
|
+
kind: "short"
|
|
67236
|
+
});
|
|
67237
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
67238
|
+
symbol: focus_account.symbol
|
|
67239
|
+
});
|
|
67240
|
+
const result = determineCompoundLongTrade({
|
|
67241
|
+
focus_short_position,
|
|
67242
|
+
focus_long_position,
|
|
67243
|
+
shortConfig,
|
|
67244
|
+
global_config: symbol_config,
|
|
67245
|
+
rr
|
|
67246
|
+
});
|
|
67247
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
67248
|
+
symbol: main_account.symbol,
|
|
67249
|
+
params: {
|
|
67250
|
+
resistance: result.resistance,
|
|
67251
|
+
support: result.support,
|
|
67252
|
+
profit_percent: result.profit_percent,
|
|
67253
|
+
risk_reward: result.result.risk_reward,
|
|
67254
|
+
risk: result.result.risk
|
|
67255
|
+
},
|
|
67256
|
+
place
|
|
67257
|
+
});
|
|
67258
|
+
return result;
|
|
67259
|
+
}
|
|
67074
67260
|
async reduceExistingPosition(payload) {
|
|
67075
67261
|
const {
|
|
67076
67262
|
main_account,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gbozee/ultimate",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.2-
|
|
4
|
+
"version": "0.0.2-131",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"build": "bun run build.ts",
|
|
36
36
|
"prepublishOnly": "bun run build",
|
|
37
37
|
"test": "bun test --timeout 300000 test",
|
|
38
|
+
"test:unit": "bun test --timeout 300000 tests/unit-tests",
|
|
38
39
|
"mcp:client": "bun run tests/mcp-client.js dist/mcp-server.js",
|
|
39
40
|
"npm:publish": "npm publish --access public --registry=https://registry.npmjs.org/"
|
|
40
41
|
},
|