Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

relative orders pump/dump detector feature to prevent loss #688

Open
vereten opened this issue Sep 14, 2019 · 7 comments
Open

relative orders pump/dump detector feature to prevent loss #688

vereten opened this issue Sep 14, 2019 · 7 comments

Comments

@vereten
Copy link

vereten commented Sep 14, 2019

Introduction to pumps and dumps

As you all know, day by day pumps and dumps happening on any exchanges. In this cases dexbots produces significant loss, because it has no protection for this case.

example of real situation and problem description

For example:
You are running bot on pair BTC/BBB at 10% spread.
Actual BBB center price is 100 SAT.
If someone pumps BBB middle price from 100 to 200 SAT that is 100% price pump. Your dexbot is running at 10% spread your dexbot will probably only once sell BBB at 105 SAT and than at dump will dexbot loss because stars buying BBB at middle price 200 at 10% spread so 195 190 180 175.. and so...

How to deal with this pump/dump problem

Add average price spread measurement.
Add configuration pump_dump_hit_spread_factor
Add pump_dump_detector() function which:

  • every time dexbot hits order, this function will check if pump or dump is not happening and dexbot worker will stop until price goes back to "normal"
    Definition of normal should be relatively computed by average spread and pump_dump_hit_spread_factor by simple match "normal" is when price change was not more than (pump_dump_hit_spread_factor * spread_average)
    Config pump_dump_hit_spread_factor is needed because pumps and dumps are not same at whole market but rather specific pair by pair.
    Also need to add very basic pump and dump detect in case when not enough measurements of average spread have not been done yet and price change was more than X%

actual status of this feature

my friend and me are working on this experimental feature also another dexbot features in our free time. We still have not implemented this in QT/GUI, so options are still hardcoded.
Our expectation 1# for this experimental code are tips in BTC/LTC/ETH/PIVX or whatever other altcoins to transparent empty addresses or directly to bitshares account.
Our expectation 2# is to get someone who gonna add pump_dump_hit_spread_factor config to QT/GUI
Our expectation 3# is to get assist by dexbot team to integrate this piece of code to dexbot.
We are still in process of testing this feature...

To make this happen, please share your feedback, ask or whatever comment.

@PermieBTS
Copy link
Collaborator

PermieBTS commented Sep 14, 2019

Are you talking about a new optional parameter for Relative Orders to be added to DEXBot that says If price moves X% within Y seconds, disable DEXBot RO Worker?

Good to hear you are working on new DEXBot features in your spare time :)

@vereten
Copy link
Author

vereten commented Sep 14, 2019

I think that no time measurement is needed, just X% of price moves. but this is only basic pump and pump detector part which is active until enough measurements of average spread are done to be able to detect pump and dump way normal ~ (pump_dump_hit_spread_factor * spread_average)
It will enable worker again when pump/dump ends or worker stays disabled in case when something unexpected happen and it will not be pump/dump.

To this feature not be too much aggressive for machine, measures happening only when order fill.

@vereten
Copy link
Author

vereten commented Sep 26, 2019

Some logic and algorithm about pump dump implementation.
First need to add some variables and configuration variables to be initialized at worker start in function:
def __init__(self, *args, **kwargs):

    # pump and dump detector
    self.pump_dump_hit = False
    self.pump_dump_center_price_last_average = None
    self.pump_dump_spread_average = 0
    self.pump_dump_spread_average_measure_num = 0
    self.pump_dump_spread_average_measure_num_min = 2 #minimum number of measurements that enables advanced pump and dump detection
    self.pump_dump_spread_average_measure_num_max = 10 #measuring average spread by last 10 price changes when bot order pass
    self.pump_dump_hit_spread_factor = 5 #pump and dump is detected if actual spread is "pump_dump_hit_spread_factor" times more than average spread
    self.pump_dump_basic_detect_percent_change = 0.28 # 28% #very basic pump and dump detect in case when not enough measurements have not been done yet and price change was more than X% (0.1==10% 0.2=20%)

Than some initialization function is needed to add.

def pump_dump_detector_init(self):
    #average center price initialization
    if self.pump_dump_center_price_last_average is None:
        self.pump_dump_center_price_last_average = self.center_price
    
    if self.pump_dump_center_price_last_average is None:
        self.pump_dump_center_price_last_average = self.get_market_center_price()

Than main pump and dump detector function itself is needed to add. This function returns true when pump or dump is detected. For now im sorry for is still in stage of been fully print debugged...

def pump_dump_detector(self):
    print("**********************************************************")
    self.pump_dump_detector_init()
    
    #temporary store values if restore needed
    tmp_pump_dump_center_price_last_average = self.pump_dump_center_price_last_average
    
    #basic computations        
    pump_dump_center_min = min(self.pump_dump_center_price_last_average, self.center_price)
    pump_dump_center_max = max(self.pump_dump_center_price_last_average, self.center_price)
    
    actual_spread = pump_dump_center_max - pump_dump_center_min
    self.pump_dump_center_price_last_average = (pump_dump_center_min + pump_dump_center_max) / 2
    
    #pump and dump detect debug
    print("pump/dump detector if: self.pump_dump_spread_average * self.pump_dump_hit_spread_factor ", self.pump_dump_spread_average, "*", self.pump_dump_hit_spread_factor , "=", (self.pump_dump_spread_average * self.pump_dump_hit_spread_factor), " < actual_spread ", actual_spread)
    print("pump/dump detector and if: self.pump_dump_spread_average_measure_num", self.pump_dump_spread_average_measure_num, " > self.pump_dump_spread_average_measure_num_min ", self.pump_dump_spread_average_measure_num_min)
    
    print("pump/dump detector if: self.center_price > (self.pump_dump_center_price_last_average * (1+self.pump_dump_basic_detect_percent_change))",
          self.center_price, ">", (self.pump_dump_center_price_last_average * (1+self.pump_dump_basic_detect_percent_change)))
    print("pump/dump detector if: self.center_price < (self.pump_dump_center_price_last_average * (1-self.pump_dump_basic_detect_percent_change))",
          self.center_price, "<", (self.pump_dump_center_price_last_average * (1-self.pump_dump_basic_detect_percent_change)))
    
    #pump and dump detect
    if self.pump_dump_spread_average_measure_num < self.pump_dump_spread_average_measure_num_min:
        #very basic pump and dump detect in case if measurements have not been done yet and price change was more than X%
        if self.center_price > (self.pump_dump_center_price_last_average * (1+self.pump_dump_basic_detect_percent_change)) or self.center_price < (self.pump_dump_center_price_last_average * (1-self.pump_dump_basic_detect_percent_change)):
            #in case of pump and dump restore values
            self.pump_dump_center_price_last_average = tmp_pump_dump_center_price_last_average
            print("pump/dump detector: Warning, waiting for pump/dump(basic) finish")
            return True
    else:
        #advanced pump and dump detection    
        if (self.pump_dump_spread_average * self.pump_dump_hit_spread_factor) < actual_spread:
            #in case of pump and dump restore values
            self.pump_dump_center_price_last_average = tmp_pump_dump_center_price_last_average
            print("pump/dump detector: Warning, waiting for pump/dump(advanced) finish")
            return True
    
    #average spread compute
    if actual_spread > 0:
        self.pump_dump_spread_average = (self.pump_dump_spread_average * self.pump_dump_spread_average_measure_num) + actual_spread
        self.pump_dump_spread_average_measure_num = self.pump_dump_spread_average_measure_num + 1
        self.pump_dump_spread_average = self.pump_dump_spread_average / self.pump_dump_spread_average_measure_num
    
    #average spread measurement number limit
    if self.pump_dump_spread_average_measure_num > self.pump_dump_spread_average_measure_num_max:
        self.pump_dump_spread_average_measure_num = self.pump_dump_spread_average_measure_num_max
    
    print("pump/dump detector: ok")
    return False

Initialization self.pump_dump_detector_init() function should be called right after enabling worker. Main pump_dump_detector() function should be called all time when update_orders() function is called to prevent adding new order when hit pump/dump.

So def update_orders(self): needs code update to return when hit pump/dump, before orders are updated and your bot will be part of pump/dump funds loss.

    self.pump_dump_hit = self.pump_dump_detector()
    if self.pump_dump_hit == True:
        return

For example insert part of code like this:

def update_orders(self):
    self.log.debug('Starting to update orders')

    # Cancel the orders before redoing them
    self.cancel_all_orders()
    self.clear_orders()

    # Recalculate buy and sell order prices
    self.calculate_order_prices()

    order_ids = []
    expected_num_orders = 0

    amount_to_buy = self.amount_to_buy
    amount_to_sell = self.amount_to_sell
    
    self.pump_dump_hit = self.pump_dump_detector()
    if self.pump_dump_hit == True:
        return

Next is to restore worker functionality when hit pump/dump ends. So def tick(self, d): needs to stand by pump/dump end detection. For example insert part of code like this:

def tick(self, d):
    """ Ticks come in on every block. We need to periodically check orders because cancelled orders
        do not triggers a market_update event
    """
    if self.pump_dump_hit == True:
        self.update_orders()

For this time. Thats all folks. Thanks for any feedback, questions, issue reports.
It will be great if you can send me any tips in any bitshares supported crypto for development of this feature directly to hsccc-5 transparent bitshares account https://wallet.bitshares.org/#/account/hsccc-5

@PermieBTS
Copy link
Collaborator

Thanks for your work on this.

I will ask the devs to take a closer look into this in the coming weeks. However due to the funding sitation this is not a priority

@vereten
Copy link
Author

vereten commented Sep 26, 2019

Thanks. Will see, maybe BTS account will make some micro-transaction tips or direct deposit tips.

This is only the first stage of feature, next stage gonna be update to take advantage of pump/dumps and make huge profits on it, by at pump/dump time making only valley-staggered sells or buys depends on case pump-to-dump or dump-to-pump.

@vereten
Copy link
Author

vereten commented Oct 6, 2019

As i can see no one was interested in this algorithm that can save you from pump/dump lost which are sometimes many % of your portfolio. For now we are canceling public development of this feature and continuing in private.

@PermieBTS
Copy link
Collaborator

PermieBTS commented Oct 6, 2019

There is interest. However, currently the CEX-DEX arbitrage feature must take priority to ensure it is developed ASAP.

Perhaps once private development is complete further down the road DEXBot can negotiate a fee to compensate you open-sourcing it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants