@@ -31,6 +31,9 @@ export type PingPongStrategyConfig = {
3131 executeAboveExpectedProfitPercent : number ;
3232 priorityFeeMicroLamports ?: number ;
3333 lock ?: boolean ;
34+ enableExpectedProfitBasedStopLoss ?: boolean ;
35+ profitBasedStopLossPercent ?: number ;
36+ onStopLossAction ?: "sell&reset" | "shutdown" | "sell&shutdown" ;
3437 shouldReset ?: boolean ;
3538 autoReset ?: {
3639 enabled : boolean ;
@@ -50,6 +53,7 @@ export const PingPongStrategy: Strategy<PingPongStrategyConfig> = {
5053 lock : false ,
5154 enableAutoSlippage : false ,
5255 enableCompounding : false ,
56+ enableExpectedProfitBasedStopLoss : false ,
5357 } ,
5458 uiHook : { } ,
5559 // dependencies: {
@@ -184,6 +188,7 @@ export const PingPongStrategy: Strategy<PingPongStrategyConfig> = {
184188 }
185189
186190 const initialToken = this . config . tokensInfo [ 0 ] ;
191+
187192 // check if lock is enabled
188193 if ( this . config . lock ) {
189194 bot . logger . info (
@@ -296,6 +301,11 @@ export const PingPongStrategy: Strategy<PingPongStrategyConfig> = {
296301 return done ( this ) ;
297302 }
298303
304+ // reset recentOutAmount to current outAmount if it's 0
305+ if ( this . config . outToken . recentOutAmount . equals ( 0 ) ) {
306+ this . config . outToken . recentOutAmount = outAmountMulti . uiValue . decimal ;
307+ }
308+
299309 if ( this . config . shouldReset ) {
300310 this . config . shouldReset = false ;
301311 this . config . outToken . recentOutAmount = outAmountMulti . uiValue . decimal ;
@@ -332,10 +342,11 @@ export const PingPongStrategy: Strategy<PingPongStrategyConfig> = {
332342 ) ;
333343 //reset
334344 this . config . outToken . recentOutAmount = outAmountMulti . uiValue . decimal ;
345+ bot . reportExpectedProfitPercent ( 0 ) ;
346+ } else {
347+ bot . reportExpectedProfitPercent ( expectedProfitPercent . toNumber ( ) ) ;
335348 }
336349
337- bot . reportExpectedProfitPercent ( expectedProfitPercent . toNumber ( ) ) ;
338-
339350 bot . logger . debug (
340351 {
341352 tradeAmount : tradeAmount ,
@@ -354,8 +365,82 @@ export const PingPongStrategy: Strategy<PingPongStrategyConfig> = {
354365 throw new Error ( "PingPongStrategy:run: no routes found" ) ;
355366 }
356367
357- const shouldExecute =
358- bot . store . getState ( ) . strategies . current . shouldExecute ;
368+ let shouldExecute = bot . store . getState ( ) . strategies . current . shouldExecute ;
369+
370+ /**
371+ * Profit based stop loss
372+ * If expected profit is lower than stop loss percent
373+ * then execute stop loss action
374+ */
375+ if (
376+ this . config . enableExpectedProfitBasedStopLoss &&
377+ this . config . profitBasedStopLossPercent &&
378+ expectedProfitPercent . toNumber ( ) <
379+ this . config . profitBasedStopLossPercent * - 1 &&
380+ isSellSide
381+ ) {
382+ bot . setStatus ( "strategy:stopLossExceeded" ) ;
383+
384+ if ( this . config . onStopLossAction === "shutdown" ) {
385+ const msg = `PingPongStrategy:run: profitBasedStopLossPercent ${ this . config . profitBasedStopLossPercent } reached, shutting down bot` ;
386+ bot . logger . info ( { runtimeId } , msg ) ;
387+ console . log ( "\n\n" + msg + "\n\n" ) ;
388+ bot . setStatus ( "!shutdown" ) ;
389+ }
390+
391+ if ( this . config . onStopLossAction === "sell&shutdown" ) {
392+ bot . logger . info (
393+ { runtimeId } ,
394+ "PingPongStrategy:run: profitBasedStopLossPercent reached, selling current out token"
395+ ) ;
396+ shouldExecute = true ;
397+
398+ bot . store . subscribe (
399+ ( s ) => s . status . value ,
400+ ( status ) => {
401+ if ( status === "history:successfulTx" ) {
402+ bot . logger . info (
403+ { runtimeId } ,
404+ "PingPongStrategy:run: profitBasedStopLossPercent reached, token sold successfully, shutting down bot"
405+ ) ;
406+ bot . setStatus ( "!shutdown" ) ;
407+ }
408+ }
409+ ) ;
410+ }
411+
412+ if ( this . config . onStopLossAction === "sell&reset" ) {
413+ bot . logger . info (
414+ { runtimeId } ,
415+ "PingPongStrategy:run: profitBasedStopLossPercent reached, selling current out token, and resetting inToken recentOutAmount"
416+ ) ;
417+
418+ // reset recent out amount for inToken
419+ this . config . inToken . recentOutAmount = new Decimal ( 0 ) ;
420+
421+ shouldExecute = true ;
422+
423+ const unsubscribe = bot . store . subscribe (
424+ ( s ) => s . status . value ,
425+ ( status ) => {
426+ if ( status === "history:successfulTx" ) {
427+ if ( ! this . config . outToken || ! this . config . inToken ) {
428+ throw new Error (
429+ "PingPongStrategy:run: this.config.outToken undefined"
430+ ) ;
431+ }
432+
433+ bot . logger . info (
434+ { runtimeId, outToken : this . config . outToken } ,
435+ "PingPongStrategy:run: profitBasedStopLossPercent reached, token sold successfully"
436+ ) ;
437+ unsubscribe ( ) ;
438+ }
439+ }
440+ ) ;
441+ }
442+ }
443+
359444 if (
360445 expectedProfitPercent . toNumber ( ) >
361446 this . config . executeAboveExpectedProfitPercent ||
@@ -380,8 +465,6 @@ export const PingPongStrategy: Strategy<PingPongStrategyConfig> = {
380465 ) ;
381466 }
382467
383- const initialToken = this . config . tokensInfo [ 0 ] ;
384-
385468 // auto slippage
386469 let customSlippageThreshold : bigint | undefined ;
387470
0 commit comments