@gbozee/ultimate 0.0.2-130 → 0.0.2-132
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 +49 -0
- package/dist/index.cjs +190 -1
- package/dist/index.d.ts +91 -0
- package/dist/index.js +190 -1
- package/dist/mcp-server.cjs +189 -1
- package/dist/mcp-server.js +189 -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,54 @@ 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 diff = short_profit * rr / short_app_config.last_value.avg_size;
|
|
2154
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - diff);
|
|
2155
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
2156
|
+
console.log({ support, resistance, short_profit });
|
|
2157
|
+
const result = getRiskReward({
|
|
2158
|
+
entry: resistance,
|
|
2159
|
+
stop: support,
|
|
2160
|
+
risk: start_risk,
|
|
2161
|
+
global_config,
|
|
2162
|
+
force_exact_risk: true
|
|
2163
|
+
});
|
|
2164
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
2165
|
+
entry: resistance,
|
|
2166
|
+
stop: support,
|
|
2167
|
+
risk_reward: result.risk_reward,
|
|
2168
|
+
risk: result.risk,
|
|
2169
|
+
symbol: shortConfig.symbol
|
|
2170
|
+
});
|
|
2171
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
2172
|
+
return {
|
|
2173
|
+
start_risk,
|
|
2174
|
+
short_profit,
|
|
2175
|
+
support: to_f(support, global_config.price_places),
|
|
2176
|
+
resistance: to_f(resistance, global_config.price_places),
|
|
2177
|
+
long_v: long_app_config.last_value,
|
|
2178
|
+
profit_percent: to_f(long_profit_percent, "%.3f"),
|
|
2179
|
+
result,
|
|
2180
|
+
short_max_size
|
|
2181
|
+
};
|
|
2182
|
+
}
|
|
2135
2183
|
// src/helpers/strategy.ts
|
|
2136
2184
|
class Strategy {
|
|
2137
2185
|
position;
|
|
@@ -2665,6 +2713,7 @@ export {
|
|
|
2665
2713
|
determineRewardFactor,
|
|
2666
2714
|
determineOptimumRisk,
|
|
2667
2715
|
determineOptimumReward,
|
|
2716
|
+
determineCompoundLongTrade,
|
|
2668
2717
|
createGapPairs,
|
|
2669
2718
|
createArray,
|
|
2670
2719
|
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,54 @@ 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 diff = short_profit * rr / short_app_config.last_value.avg_size;
|
|
54491
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - diff);
|
|
54492
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
54493
|
+
console.log({ support, resistance, short_profit });
|
|
54494
|
+
const result = getRiskReward({
|
|
54495
|
+
entry: resistance,
|
|
54496
|
+
stop: support,
|
|
54497
|
+
risk: start_risk,
|
|
54498
|
+
global_config,
|
|
54499
|
+
force_exact_risk: true
|
|
54500
|
+
});
|
|
54501
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
54502
|
+
entry: resistance,
|
|
54503
|
+
stop: support,
|
|
54504
|
+
risk_reward: result.risk_reward,
|
|
54505
|
+
risk: result.risk,
|
|
54506
|
+
symbol: shortConfig.symbol
|
|
54507
|
+
});
|
|
54508
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
54509
|
+
return {
|
|
54510
|
+
start_risk,
|
|
54511
|
+
short_profit,
|
|
54512
|
+
support: to_f2(support, global_config.price_places),
|
|
54513
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
54514
|
+
long_v: long_app_config.last_value,
|
|
54515
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
54516
|
+
result,
|
|
54517
|
+
short_max_size
|
|
54518
|
+
};
|
|
54519
|
+
}
|
|
54440
54520
|
|
|
54441
54521
|
// src/helpers/strategy.ts
|
|
54442
54522
|
class Strategy {
|
|
@@ -58520,6 +58600,10 @@ class ExchangeAccount {
|
|
|
58520
58600
|
symbol,
|
|
58521
58601
|
kind
|
|
58522
58602
|
});
|
|
58603
|
+
await this.updateRiskOnEmpty({
|
|
58604
|
+
symbol,
|
|
58605
|
+
kind
|
|
58606
|
+
});
|
|
58523
58607
|
}
|
|
58524
58608
|
const long_config = await this.getPositionConfig({
|
|
58525
58609
|
symbol,
|
|
@@ -59011,17 +59095,53 @@ class ExchangeAccount {
|
|
|
59011
59095
|
if (position2?.expand?.config) {
|
|
59012
59096
|
const config2 = position2.expand.config;
|
|
59013
59097
|
let _profit = config2.profit;
|
|
59098
|
+
let risk = config2.risk;
|
|
59099
|
+
let next_risk = position2.next_risk;
|
|
59014
59100
|
let _profit_percent = config2?.profit_percent;
|
|
59015
59101
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
59016
59102
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
59103
|
+
next_risk = risk + _profit;
|
|
59017
59104
|
}
|
|
59018
59105
|
await this.app_db.update_db_position(position2, {
|
|
59019
|
-
target_pnl: _profit
|
|
59106
|
+
target_pnl: _profit,
|
|
59107
|
+
next_risk
|
|
59020
59108
|
});
|
|
59021
59109
|
return _profit;
|
|
59022
59110
|
}
|
|
59023
59111
|
return 0;
|
|
59024
59112
|
}
|
|
59113
|
+
async updateRiskOnEmpty(payload) {
|
|
59114
|
+
const { symbol, kind } = payload;
|
|
59115
|
+
const position2 = await this.syncAccount({
|
|
59116
|
+
symbol,
|
|
59117
|
+
kind
|
|
59118
|
+
});
|
|
59119
|
+
if (position2 && position2.quantity === 0) {
|
|
59120
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
59121
|
+
await this.getPositionConfig({
|
|
59122
|
+
symbol,
|
|
59123
|
+
kind,
|
|
59124
|
+
params: {
|
|
59125
|
+
risk: position2.next_risk
|
|
59126
|
+
}
|
|
59127
|
+
});
|
|
59128
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
59129
|
+
return {
|
|
59130
|
+
updated: true,
|
|
59131
|
+
symbol,
|
|
59132
|
+
kind,
|
|
59133
|
+
old_risk: position2.expand?.config?.risk,
|
|
59134
|
+
new_risk: position2.next_risk
|
|
59135
|
+
};
|
|
59136
|
+
}
|
|
59137
|
+
}
|
|
59138
|
+
return {
|
|
59139
|
+
updated: false,
|
|
59140
|
+
symbol,
|
|
59141
|
+
kind,
|
|
59142
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
59143
|
+
};
|
|
59144
|
+
}
|
|
59025
59145
|
async updateGoodHedgeConfig(payload) {
|
|
59026
59146
|
const {
|
|
59027
59147
|
params,
|
|
@@ -60077,6 +60197,38 @@ class ExchangeAccount {
|
|
|
60077
60197
|
decimal_places: symbol_config.decimal_places
|
|
60078
60198
|
});
|
|
60079
60199
|
}
|
|
60200
|
+
async placeCompoundShortTrade(payload) {
|
|
60201
|
+
}
|
|
60202
|
+
async placeCompoundLongTrade(payload) {
|
|
60203
|
+
const { symbol, params, place = false } = payload;
|
|
60204
|
+
if (place) {
|
|
60205
|
+
await this.getPositionConfig({
|
|
60206
|
+
symbol,
|
|
60207
|
+
kind: "long",
|
|
60208
|
+
params: {
|
|
60209
|
+
entry: params.resistance,
|
|
60210
|
+
stop: params.support,
|
|
60211
|
+
risk_reward: params.risk_reward,
|
|
60212
|
+
risk: params.risk,
|
|
60213
|
+
profit_percent: params.profit_percent
|
|
60214
|
+
}
|
|
60215
|
+
});
|
|
60216
|
+
const short_position = await this.syncAccount({
|
|
60217
|
+
symbol,
|
|
60218
|
+
kind: "short",
|
|
60219
|
+
as_view: true
|
|
60220
|
+
});
|
|
60221
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
60222
|
+
await this.placeTrade({
|
|
60223
|
+
symbol,
|
|
60224
|
+
kind: "long",
|
|
60225
|
+
ignore_config: true,
|
|
60226
|
+
limit: true,
|
|
60227
|
+
place: true
|
|
60228
|
+
});
|
|
60229
|
+
}
|
|
60230
|
+
}
|
|
60231
|
+
}
|
|
60080
60232
|
}
|
|
60081
60233
|
function getExchangeKlass(exchange) {
|
|
60082
60234
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -60417,6 +60569,43 @@ class App {
|
|
|
60417
60569
|
});
|
|
60418
60570
|
return result;
|
|
60419
60571
|
}
|
|
60572
|
+
async compoundLongTrade(payload) {
|
|
60573
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
60574
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
60575
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
60576
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
60577
|
+
symbol: focus_account.symbol,
|
|
60578
|
+
as_view: true
|
|
60579
|
+
});
|
|
60580
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
60581
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
60582
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
60583
|
+
symbol: main_account.symbol,
|
|
60584
|
+
kind: "short"
|
|
60585
|
+
});
|
|
60586
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
60587
|
+
symbol: focus_account.symbol
|
|
60588
|
+
});
|
|
60589
|
+
const result = determineCompoundLongTrade({
|
|
60590
|
+
focus_short_position,
|
|
60591
|
+
focus_long_position,
|
|
60592
|
+
shortConfig,
|
|
60593
|
+
global_config: symbol_config,
|
|
60594
|
+
rr
|
|
60595
|
+
});
|
|
60596
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
60597
|
+
symbol: main_account.symbol,
|
|
60598
|
+
params: {
|
|
60599
|
+
resistance: result.resistance,
|
|
60600
|
+
support: result.support,
|
|
60601
|
+
profit_percent: result.profit_percent,
|
|
60602
|
+
risk_reward: result.result.risk_reward,
|
|
60603
|
+
risk: result.result.risk
|
|
60604
|
+
},
|
|
60605
|
+
place
|
|
60606
|
+
});
|
|
60607
|
+
return result;
|
|
60608
|
+
}
|
|
60420
60609
|
async reduceExistingPosition(payload) {
|
|
60421
60610
|
const {
|
|
60422
60611
|
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,54 @@ 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 diff = short_profit * rr / short_app_config.last_value.avg_size;
|
|
54439
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - diff);
|
|
54440
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
54441
|
+
console.log({ support, resistance, short_profit });
|
|
54442
|
+
const result = getRiskReward({
|
|
54443
|
+
entry: resistance,
|
|
54444
|
+
stop: support,
|
|
54445
|
+
risk: start_risk,
|
|
54446
|
+
global_config,
|
|
54447
|
+
force_exact_risk: true
|
|
54448
|
+
});
|
|
54449
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
54450
|
+
entry: resistance,
|
|
54451
|
+
stop: support,
|
|
54452
|
+
risk_reward: result.risk_reward,
|
|
54453
|
+
risk: result.risk,
|
|
54454
|
+
symbol: shortConfig.symbol
|
|
54455
|
+
});
|
|
54456
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
54457
|
+
return {
|
|
54458
|
+
start_risk,
|
|
54459
|
+
short_profit,
|
|
54460
|
+
support: to_f2(support, global_config.price_places),
|
|
54461
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
54462
|
+
long_v: long_app_config.last_value,
|
|
54463
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
54464
|
+
result,
|
|
54465
|
+
short_max_size
|
|
54466
|
+
};
|
|
54467
|
+
}
|
|
54389
54468
|
|
|
54390
54469
|
// src/helpers/strategy.ts
|
|
54391
54470
|
class Strategy {
|
|
@@ -58469,6 +58548,10 @@ class ExchangeAccount {
|
|
|
58469
58548
|
symbol,
|
|
58470
58549
|
kind
|
|
58471
58550
|
});
|
|
58551
|
+
await this.updateRiskOnEmpty({
|
|
58552
|
+
symbol,
|
|
58553
|
+
kind
|
|
58554
|
+
});
|
|
58472
58555
|
}
|
|
58473
58556
|
const long_config = await this.getPositionConfig({
|
|
58474
58557
|
symbol,
|
|
@@ -58960,17 +59043,53 @@ class ExchangeAccount {
|
|
|
58960
59043
|
if (position2?.expand?.config) {
|
|
58961
59044
|
const config2 = position2.expand.config;
|
|
58962
59045
|
let _profit = config2.profit;
|
|
59046
|
+
let risk = config2.risk;
|
|
59047
|
+
let next_risk = position2.next_risk;
|
|
58963
59048
|
let _profit_percent = config2?.profit_percent;
|
|
58964
59049
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
58965
59050
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
59051
|
+
next_risk = risk + _profit;
|
|
58966
59052
|
}
|
|
58967
59053
|
await this.app_db.update_db_position(position2, {
|
|
58968
|
-
target_pnl: _profit
|
|
59054
|
+
target_pnl: _profit,
|
|
59055
|
+
next_risk
|
|
58969
59056
|
});
|
|
58970
59057
|
return _profit;
|
|
58971
59058
|
}
|
|
58972
59059
|
return 0;
|
|
58973
59060
|
}
|
|
59061
|
+
async updateRiskOnEmpty(payload) {
|
|
59062
|
+
const { symbol, kind } = payload;
|
|
59063
|
+
const position2 = await this.syncAccount({
|
|
59064
|
+
symbol,
|
|
59065
|
+
kind
|
|
59066
|
+
});
|
|
59067
|
+
if (position2 && position2.quantity === 0) {
|
|
59068
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
59069
|
+
await this.getPositionConfig({
|
|
59070
|
+
symbol,
|
|
59071
|
+
kind,
|
|
59072
|
+
params: {
|
|
59073
|
+
risk: position2.next_risk
|
|
59074
|
+
}
|
|
59075
|
+
});
|
|
59076
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
59077
|
+
return {
|
|
59078
|
+
updated: true,
|
|
59079
|
+
symbol,
|
|
59080
|
+
kind,
|
|
59081
|
+
old_risk: position2.expand?.config?.risk,
|
|
59082
|
+
new_risk: position2.next_risk
|
|
59083
|
+
};
|
|
59084
|
+
}
|
|
59085
|
+
}
|
|
59086
|
+
return {
|
|
59087
|
+
updated: false,
|
|
59088
|
+
symbol,
|
|
59089
|
+
kind,
|
|
59090
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
59091
|
+
};
|
|
59092
|
+
}
|
|
58974
59093
|
async updateGoodHedgeConfig(payload) {
|
|
58975
59094
|
const {
|
|
58976
59095
|
params,
|
|
@@ -60026,6 +60145,38 @@ class ExchangeAccount {
|
|
|
60026
60145
|
decimal_places: symbol_config.decimal_places
|
|
60027
60146
|
});
|
|
60028
60147
|
}
|
|
60148
|
+
async placeCompoundShortTrade(payload) {
|
|
60149
|
+
}
|
|
60150
|
+
async placeCompoundLongTrade(payload) {
|
|
60151
|
+
const { symbol, params, place = false } = payload;
|
|
60152
|
+
if (place) {
|
|
60153
|
+
await this.getPositionConfig({
|
|
60154
|
+
symbol,
|
|
60155
|
+
kind: "long",
|
|
60156
|
+
params: {
|
|
60157
|
+
entry: params.resistance,
|
|
60158
|
+
stop: params.support,
|
|
60159
|
+
risk_reward: params.risk_reward,
|
|
60160
|
+
risk: params.risk,
|
|
60161
|
+
profit_percent: params.profit_percent
|
|
60162
|
+
}
|
|
60163
|
+
});
|
|
60164
|
+
const short_position = await this.syncAccount({
|
|
60165
|
+
symbol,
|
|
60166
|
+
kind: "short",
|
|
60167
|
+
as_view: true
|
|
60168
|
+
});
|
|
60169
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
60170
|
+
await this.placeTrade({
|
|
60171
|
+
symbol,
|
|
60172
|
+
kind: "long",
|
|
60173
|
+
ignore_config: true,
|
|
60174
|
+
limit: true,
|
|
60175
|
+
place: true
|
|
60176
|
+
});
|
|
60177
|
+
}
|
|
60178
|
+
}
|
|
60179
|
+
}
|
|
60029
60180
|
}
|
|
60030
60181
|
function getExchangeKlass(exchange) {
|
|
60031
60182
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -60366,6 +60517,43 @@ class App {
|
|
|
60366
60517
|
});
|
|
60367
60518
|
return result;
|
|
60368
60519
|
}
|
|
60520
|
+
async compoundLongTrade(payload) {
|
|
60521
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
60522
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
60523
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
60524
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
60525
|
+
symbol: focus_account.symbol,
|
|
60526
|
+
as_view: true
|
|
60527
|
+
});
|
|
60528
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
60529
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
60530
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
60531
|
+
symbol: main_account.symbol,
|
|
60532
|
+
kind: "short"
|
|
60533
|
+
});
|
|
60534
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
60535
|
+
symbol: focus_account.symbol
|
|
60536
|
+
});
|
|
60537
|
+
const result = determineCompoundLongTrade({
|
|
60538
|
+
focus_short_position,
|
|
60539
|
+
focus_long_position,
|
|
60540
|
+
shortConfig,
|
|
60541
|
+
global_config: symbol_config,
|
|
60542
|
+
rr
|
|
60543
|
+
});
|
|
60544
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
60545
|
+
symbol: main_account.symbol,
|
|
60546
|
+
params: {
|
|
60547
|
+
resistance: result.resistance,
|
|
60548
|
+
support: result.support,
|
|
60549
|
+
profit_percent: result.profit_percent,
|
|
60550
|
+
risk_reward: result.result.risk_reward,
|
|
60551
|
+
risk: result.result.risk
|
|
60552
|
+
},
|
|
60553
|
+
place
|
|
60554
|
+
});
|
|
60555
|
+
return result;
|
|
60556
|
+
}
|
|
60369
60557
|
async reduceExistingPosition(payload) {
|
|
60370
60558
|
const {
|
|
60371
60559
|
main_account,
|
|
@@ -60507,6 +60695,7 @@ export {
|
|
|
60507
60695
|
determineRewardFactor,
|
|
60508
60696
|
determineOptimumRisk,
|
|
60509
60697
|
determineOptimumReward,
|
|
60698
|
+
determineCompoundLongTrade,
|
|
60510
60699
|
exports_database as database,
|
|
60511
60700
|
createArray,
|
|
60512
60701
|
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,54 @@ 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 diff = short_profit * rr / short_app_config.last_value.avg_size;
|
|
61167
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - diff);
|
|
61168
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
61169
|
+
console.log({ support, resistance, short_profit });
|
|
61170
|
+
const result = getRiskReward({
|
|
61171
|
+
entry: resistance,
|
|
61172
|
+
stop: support,
|
|
61173
|
+
risk: start_risk,
|
|
61174
|
+
global_config,
|
|
61175
|
+
force_exact_risk: true
|
|
61176
|
+
});
|
|
61177
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
61178
|
+
entry: resistance,
|
|
61179
|
+
stop: support,
|
|
61180
|
+
risk_reward: result.risk_reward,
|
|
61181
|
+
risk: result.risk,
|
|
61182
|
+
symbol: shortConfig.symbol
|
|
61183
|
+
});
|
|
61184
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
61185
|
+
return {
|
|
61186
|
+
start_risk,
|
|
61187
|
+
short_profit,
|
|
61188
|
+
support: to_f2(support, global_config.price_places),
|
|
61189
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
61190
|
+
long_v: long_app_config.last_value,
|
|
61191
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
61192
|
+
result,
|
|
61193
|
+
short_max_size
|
|
61194
|
+
};
|
|
61195
|
+
}
|
|
61117
61196
|
|
|
61118
61197
|
// src/helpers/strategy.ts
|
|
61119
61198
|
class Strategy {
|
|
@@ -65197,6 +65276,10 @@ class ExchangeAccount {
|
|
|
65197
65276
|
symbol,
|
|
65198
65277
|
kind
|
|
65199
65278
|
});
|
|
65279
|
+
await this.updateRiskOnEmpty({
|
|
65280
|
+
symbol,
|
|
65281
|
+
kind
|
|
65282
|
+
});
|
|
65200
65283
|
}
|
|
65201
65284
|
const long_config = await this.getPositionConfig({
|
|
65202
65285
|
symbol,
|
|
@@ -65688,17 +65771,53 @@ class ExchangeAccount {
|
|
|
65688
65771
|
if (position2?.expand?.config) {
|
|
65689
65772
|
const config2 = position2.expand.config;
|
|
65690
65773
|
let _profit = config2.profit;
|
|
65774
|
+
let risk = config2.risk;
|
|
65775
|
+
let next_risk = position2.next_risk;
|
|
65691
65776
|
let _profit_percent = config2?.profit_percent;
|
|
65692
65777
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
65693
65778
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
65779
|
+
next_risk = risk + _profit;
|
|
65694
65780
|
}
|
|
65695
65781
|
await this.app_db.update_db_position(position2, {
|
|
65696
|
-
target_pnl: _profit
|
|
65782
|
+
target_pnl: _profit,
|
|
65783
|
+
next_risk
|
|
65697
65784
|
});
|
|
65698
65785
|
return _profit;
|
|
65699
65786
|
}
|
|
65700
65787
|
return 0;
|
|
65701
65788
|
}
|
|
65789
|
+
async updateRiskOnEmpty(payload) {
|
|
65790
|
+
const { symbol, kind } = payload;
|
|
65791
|
+
const position2 = await this.syncAccount({
|
|
65792
|
+
symbol,
|
|
65793
|
+
kind
|
|
65794
|
+
});
|
|
65795
|
+
if (position2 && position2.quantity === 0) {
|
|
65796
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
65797
|
+
await this.getPositionConfig({
|
|
65798
|
+
symbol,
|
|
65799
|
+
kind,
|
|
65800
|
+
params: {
|
|
65801
|
+
risk: position2.next_risk
|
|
65802
|
+
}
|
|
65803
|
+
});
|
|
65804
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
65805
|
+
return {
|
|
65806
|
+
updated: true,
|
|
65807
|
+
symbol,
|
|
65808
|
+
kind,
|
|
65809
|
+
old_risk: position2.expand?.config?.risk,
|
|
65810
|
+
new_risk: position2.next_risk
|
|
65811
|
+
};
|
|
65812
|
+
}
|
|
65813
|
+
}
|
|
65814
|
+
return {
|
|
65815
|
+
updated: false,
|
|
65816
|
+
symbol,
|
|
65817
|
+
kind,
|
|
65818
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
65819
|
+
};
|
|
65820
|
+
}
|
|
65702
65821
|
async updateGoodHedgeConfig(payload) {
|
|
65703
65822
|
const {
|
|
65704
65823
|
params,
|
|
@@ -66754,6 +66873,38 @@ class ExchangeAccount {
|
|
|
66754
66873
|
decimal_places: symbol_config.decimal_places
|
|
66755
66874
|
});
|
|
66756
66875
|
}
|
|
66876
|
+
async placeCompoundShortTrade(payload) {
|
|
66877
|
+
}
|
|
66878
|
+
async placeCompoundLongTrade(payload) {
|
|
66879
|
+
const { symbol, params, place = false } = payload;
|
|
66880
|
+
if (place) {
|
|
66881
|
+
await this.getPositionConfig({
|
|
66882
|
+
symbol,
|
|
66883
|
+
kind: "long",
|
|
66884
|
+
params: {
|
|
66885
|
+
entry: params.resistance,
|
|
66886
|
+
stop: params.support,
|
|
66887
|
+
risk_reward: params.risk_reward,
|
|
66888
|
+
risk: params.risk,
|
|
66889
|
+
profit_percent: params.profit_percent
|
|
66890
|
+
}
|
|
66891
|
+
});
|
|
66892
|
+
const short_position = await this.syncAccount({
|
|
66893
|
+
symbol,
|
|
66894
|
+
kind: "short",
|
|
66895
|
+
as_view: true
|
|
66896
|
+
});
|
|
66897
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
66898
|
+
await this.placeTrade({
|
|
66899
|
+
symbol,
|
|
66900
|
+
kind: "long",
|
|
66901
|
+
ignore_config: true,
|
|
66902
|
+
limit: true,
|
|
66903
|
+
place: true
|
|
66904
|
+
});
|
|
66905
|
+
}
|
|
66906
|
+
}
|
|
66907
|
+
}
|
|
66757
66908
|
}
|
|
66758
66909
|
function getExchangeKlass(exchange) {
|
|
66759
66910
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -67094,6 +67245,43 @@ class App {
|
|
|
67094
67245
|
});
|
|
67095
67246
|
return result;
|
|
67096
67247
|
}
|
|
67248
|
+
async compoundLongTrade(payload) {
|
|
67249
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
67250
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
67251
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
67252
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
67253
|
+
symbol: focus_account.symbol,
|
|
67254
|
+
as_view: true
|
|
67255
|
+
});
|
|
67256
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
67257
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
67258
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
67259
|
+
symbol: main_account.symbol,
|
|
67260
|
+
kind: "short"
|
|
67261
|
+
});
|
|
67262
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
67263
|
+
symbol: focus_account.symbol
|
|
67264
|
+
});
|
|
67265
|
+
const result = determineCompoundLongTrade({
|
|
67266
|
+
focus_short_position,
|
|
67267
|
+
focus_long_position,
|
|
67268
|
+
shortConfig,
|
|
67269
|
+
global_config: symbol_config,
|
|
67270
|
+
rr
|
|
67271
|
+
});
|
|
67272
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
67273
|
+
symbol: main_account.symbol,
|
|
67274
|
+
params: {
|
|
67275
|
+
resistance: result.resistance,
|
|
67276
|
+
support: result.support,
|
|
67277
|
+
profit_percent: result.profit_percent,
|
|
67278
|
+
risk_reward: result.result.risk_reward,
|
|
67279
|
+
risk: result.result.risk
|
|
67280
|
+
},
|
|
67281
|
+
place
|
|
67282
|
+
});
|
|
67283
|
+
return result;
|
|
67284
|
+
}
|
|
67097
67285
|
async reduceExistingPosition(payload) {
|
|
67098
67286
|
const {
|
|
67099
67287
|
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,54 @@ 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 diff = short_profit * rr / short_app_config.last_value.avg_size;
|
|
61144
|
+
const support = Math.abs(short_app_config.last_value.avg_entry - diff);
|
|
61145
|
+
const resistance = focus_short_position.next_order || focus_long_position.take_profit;
|
|
61146
|
+
console.log({ support, resistance, short_profit });
|
|
61147
|
+
const result = getRiskReward({
|
|
61148
|
+
entry: resistance,
|
|
61149
|
+
stop: support,
|
|
61150
|
+
risk: start_risk,
|
|
61151
|
+
global_config,
|
|
61152
|
+
force_exact_risk: true
|
|
61153
|
+
});
|
|
61154
|
+
const long_app_config = buildAppConfig(global_config, {
|
|
61155
|
+
entry: resistance,
|
|
61156
|
+
stop: support,
|
|
61157
|
+
risk_reward: result.risk_reward,
|
|
61158
|
+
risk: result.risk,
|
|
61159
|
+
symbol: shortConfig.symbol
|
|
61160
|
+
});
|
|
61161
|
+
const long_profit_percent = start_risk * 2 * 100 / (long_app_config.last_value.avg_size * long_app_config.last_value.avg_entry);
|
|
61162
|
+
return {
|
|
61163
|
+
start_risk,
|
|
61164
|
+
short_profit,
|
|
61165
|
+
support: to_f2(support, global_config.price_places),
|
|
61166
|
+
resistance: to_f2(resistance, global_config.price_places),
|
|
61167
|
+
long_v: long_app_config.last_value,
|
|
61168
|
+
profit_percent: to_f2(long_profit_percent, "%.3f"),
|
|
61169
|
+
result,
|
|
61170
|
+
short_max_size
|
|
61171
|
+
};
|
|
61172
|
+
}
|
|
61094
61173
|
|
|
61095
61174
|
// src/helpers/strategy.ts
|
|
61096
61175
|
class Strategy {
|
|
@@ -65174,6 +65253,10 @@ class ExchangeAccount {
|
|
|
65174
65253
|
symbol,
|
|
65175
65254
|
kind
|
|
65176
65255
|
});
|
|
65256
|
+
await this.updateRiskOnEmpty({
|
|
65257
|
+
symbol,
|
|
65258
|
+
kind
|
|
65259
|
+
});
|
|
65177
65260
|
}
|
|
65178
65261
|
const long_config = await this.getPositionConfig({
|
|
65179
65262
|
symbol,
|
|
@@ -65665,17 +65748,53 @@ class ExchangeAccount {
|
|
|
65665
65748
|
if (position2?.expand?.config) {
|
|
65666
65749
|
const config2 = position2.expand.config;
|
|
65667
65750
|
let _profit = config2.profit;
|
|
65751
|
+
let risk = config2.risk;
|
|
65752
|
+
let next_risk = position2.next_risk;
|
|
65668
65753
|
let _profit_percent = config2?.profit_percent;
|
|
65669
65754
|
if (_profit_percent && (position2?.quantity || 0) > 0) {
|
|
65670
65755
|
_profit = to_f2(position2.quantity * _profit_percent * position2.entry / 100);
|
|
65756
|
+
next_risk = risk + _profit;
|
|
65671
65757
|
}
|
|
65672
65758
|
await this.app_db.update_db_position(position2, {
|
|
65673
|
-
target_pnl: _profit
|
|
65759
|
+
target_pnl: _profit,
|
|
65760
|
+
next_risk
|
|
65674
65761
|
});
|
|
65675
65762
|
return _profit;
|
|
65676
65763
|
}
|
|
65677
65764
|
return 0;
|
|
65678
65765
|
}
|
|
65766
|
+
async updateRiskOnEmpty(payload) {
|
|
65767
|
+
const { symbol, kind } = payload;
|
|
65768
|
+
const position2 = await this.syncAccount({
|
|
65769
|
+
symbol,
|
|
65770
|
+
kind
|
|
65771
|
+
});
|
|
65772
|
+
if (position2 && position2.quantity === 0) {
|
|
65773
|
+
if (position2.next_risk && position2.next_risk > 0) {
|
|
65774
|
+
await this.getPositionConfig({
|
|
65775
|
+
symbol,
|
|
65776
|
+
kind,
|
|
65777
|
+
params: {
|
|
65778
|
+
risk: position2.next_risk
|
|
65779
|
+
}
|
|
65780
|
+
});
|
|
65781
|
+
console.log(`Updated ${kind} position config for ${symbol}: risk = ${position2.next_risk}`);
|
|
65782
|
+
return {
|
|
65783
|
+
updated: true,
|
|
65784
|
+
symbol,
|
|
65785
|
+
kind,
|
|
65786
|
+
old_risk: position2.expand?.config?.risk,
|
|
65787
|
+
new_risk: position2.next_risk
|
|
65788
|
+
};
|
|
65789
|
+
}
|
|
65790
|
+
}
|
|
65791
|
+
return {
|
|
65792
|
+
updated: false,
|
|
65793
|
+
symbol,
|
|
65794
|
+
kind,
|
|
65795
|
+
reason: position2 ? position2.quantity > 0 ? "Position not empty" : "No next_risk available" : "Position not found"
|
|
65796
|
+
};
|
|
65797
|
+
}
|
|
65679
65798
|
async updateGoodHedgeConfig(payload) {
|
|
65680
65799
|
const {
|
|
65681
65800
|
params,
|
|
@@ -66731,6 +66850,38 @@ class ExchangeAccount {
|
|
|
66731
66850
|
decimal_places: symbol_config.decimal_places
|
|
66732
66851
|
});
|
|
66733
66852
|
}
|
|
66853
|
+
async placeCompoundShortTrade(payload) {
|
|
66854
|
+
}
|
|
66855
|
+
async placeCompoundLongTrade(payload) {
|
|
66856
|
+
const { symbol, params, place = false } = payload;
|
|
66857
|
+
if (place) {
|
|
66858
|
+
await this.getPositionConfig({
|
|
66859
|
+
symbol,
|
|
66860
|
+
kind: "long",
|
|
66861
|
+
params: {
|
|
66862
|
+
entry: params.resistance,
|
|
66863
|
+
stop: params.support,
|
|
66864
|
+
risk_reward: params.risk_reward,
|
|
66865
|
+
risk: params.risk,
|
|
66866
|
+
profit_percent: params.profit_percent
|
|
66867
|
+
}
|
|
66868
|
+
});
|
|
66869
|
+
const short_position = await this.syncAccount({
|
|
66870
|
+
symbol,
|
|
66871
|
+
kind: "short",
|
|
66872
|
+
as_view: true
|
|
66873
|
+
});
|
|
66874
|
+
if (short_position.quantity >= short_position.avg_qty) {
|
|
66875
|
+
await this.placeTrade({
|
|
66876
|
+
symbol,
|
|
66877
|
+
kind: "long",
|
|
66878
|
+
ignore_config: true,
|
|
66879
|
+
limit: true,
|
|
66880
|
+
place: true
|
|
66881
|
+
});
|
|
66882
|
+
}
|
|
66883
|
+
}
|
|
66884
|
+
}
|
|
66734
66885
|
}
|
|
66735
66886
|
function getExchangeKlass(exchange) {
|
|
66736
66887
|
const func = exchange === "binance" ? BinanceExchange : BybitExchange;
|
|
@@ -67071,6 +67222,43 @@ class App {
|
|
|
67071
67222
|
});
|
|
67072
67223
|
return result;
|
|
67073
67224
|
}
|
|
67225
|
+
async compoundLongTrade(payload) {
|
|
67226
|
+
const { main_account, focus_account, rr, place } = payload;
|
|
67227
|
+
const main_exchange_account = await this.getExchangeAccount(main_account);
|
|
67228
|
+
const focus_exchange_account = await this.getExchangeAccount(focus_account);
|
|
67229
|
+
const focus_position = await focus_exchange_account.syncAccount({
|
|
67230
|
+
symbol: focus_account.symbol,
|
|
67231
|
+
as_view: true
|
|
67232
|
+
});
|
|
67233
|
+
const focus_long_position = focus_position.find((k) => k.kind === "long");
|
|
67234
|
+
const focus_short_position = focus_position.find((k) => k.kind === "short");
|
|
67235
|
+
const shortConfig = await main_exchange_account.getPositionConfig({
|
|
67236
|
+
symbol: main_account.symbol,
|
|
67237
|
+
kind: "short"
|
|
67238
|
+
});
|
|
67239
|
+
const symbol_config = await focus_exchange_account.recomputeSymbolConfig({
|
|
67240
|
+
symbol: focus_account.symbol
|
|
67241
|
+
});
|
|
67242
|
+
const result = determineCompoundLongTrade({
|
|
67243
|
+
focus_short_position,
|
|
67244
|
+
focus_long_position,
|
|
67245
|
+
shortConfig,
|
|
67246
|
+
global_config: symbol_config,
|
|
67247
|
+
rr
|
|
67248
|
+
});
|
|
67249
|
+
await main_exchange_account.placeCompoundLongTrade({
|
|
67250
|
+
symbol: main_account.symbol,
|
|
67251
|
+
params: {
|
|
67252
|
+
resistance: result.resistance,
|
|
67253
|
+
support: result.support,
|
|
67254
|
+
profit_percent: result.profit_percent,
|
|
67255
|
+
risk_reward: result.result.risk_reward,
|
|
67256
|
+
risk: result.result.risk
|
|
67257
|
+
},
|
|
67258
|
+
place
|
|
67259
|
+
});
|
|
67260
|
+
return result;
|
|
67261
|
+
}
|
|
67074
67262
|
async reduceExistingPosition(payload) {
|
|
67075
67263
|
const {
|
|
67076
67264
|
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-132",
|
|
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
|
},
|