Example #1
0
      void cancel_all_shorts()
      {
         for( auto short_itr = _db_impl._short_db.begin(); short_itr.valid(); ++short_itr )
         {
             const market_index_key market_idx = short_itr.key();
             const order_record order_rec = short_itr.value();
             _current_bid = market_order( short_order, market_idx, order_rec );

             // Initialize the market transaction
             market_transaction mtrx;
             mtrx.bid_owner = _current_bid->get_owner();
             mtrx.bid_type = short_order;

             cancel_current_short( mtrx, market_idx.order_price.quote_asset_id );
             push_market_transaction( mtrx );
         }
      }
Example #2
0
  void market_engine_v7::cancel_high_apr_shorts()
  {
      static const fc::uint128 max_apr = fc::uint128( BTS_BLOCKCHAIN_MAX_SHORT_APR_PCT ) * FC_REAL128_PRECISION / 100;

      for( auto short_itr = _db_impl._short_db.begin(); short_itr.valid(); ++short_itr )
      {
          const market_index_key market_idx = short_itr.key();
          if( market_idx.order_price.ratio <= max_apr )
              continue;

          const order_record order_rec = short_itr.value();
          _current_bid = market_order( short_order, market_idx, order_rec, order_rec.balance, market_idx.order_price );

          // Initialize the market transaction
          market_transaction mtrx;
          mtrx.bid_index.owner = _current_bid->get_owner();
          mtrx.bid_type = short_order;

          cancel_current_short( mtrx, market_idx.order_price.quote_asset_id );
          push_market_transaction( mtrx );
      }

      _pending_state->apply_changes();
  }
Example #3
0
      void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp )
      {
         try
         {
             const uint32_t pending_block_num = _pending_state->get_head_block_num();

             _quote_id = quote_id;
             _base_id = base_id;
             oasset_record quote_asset = _pending_state->get_asset_record( _quote_id );
             oasset_record base_asset = _pending_state->get_asset_record( _base_id );
             FC_ASSERT( quote_asset.valid() && base_asset.valid() );

             // The order book is sorted from low to high price. So to get the last item (highest bid),
             // we need to go to the first item in the next market class and then back up one
             const price next_pair = (base_id+1 == quote_id) ? price( 0, quote_id+1, 0 ) : price( 0, quote_id, base_id+1 );
             _bid_itr        = _db_impl._bid_db.lower_bound( market_index_key( next_pair ) );
             _ask_itr        = _db_impl._ask_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) );
             _short_itr      = _db_impl._short_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) );
             _collateral_itr = _db_impl._collateral_db.lower_bound( market_index_key( next_pair ) );

             if( !_ask_itr.valid() )
             {
                wlog( "ask iter invalid..." );
                _ask_itr = _db_impl._ask_db.begin();
             }

             if( _bid_itr.valid() )   --_bid_itr;
             else _bid_itr = _db_impl._bid_db.last();

             if( _collateral_itr.valid() )   --_collateral_itr;
             else _collateral_itr = _db_impl._collateral_db.last();

             asset trading_volume(0, base_id);

             // Set initial market status
             {
                 omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id );
                 if( !market_stat ) market_stat = market_status( quote_id, base_id, 0, 0 );
                 _market_stat = *market_stat;
             }

             price min_cover_ask;
             price opening_price;
             price closing_price;

             const oprice median_feed_price = _db_impl.self->get_median_delegate_price( quote_id, base_id );
             if( quote_asset->is_market_issued() )
             {
                 // If bootstrapping market for the very first time
                 if( _market_stat.center_price.ratio == fc::uint128_t() )
                 {
                     if( median_feed_price.valid() )
                         _market_stat.center_price = *median_feed_price;
                     else 
                         FC_CAPTURE_AND_THROW( insufficient_feeds, (quote_id) );
                 }

                 min_cover_ask = _market_stat.minimum_ask();
             }

             int last_orders_filled = -1;

             bool order_did_execute = false;

             // prime the pump, to make sure that margin calls (asks) have a bid to check against.
             get_next_bid(); get_next_ask();
             idump( (_current_bid)(_current_ask) );
             while( get_next_bid() && get_next_ask() )
             {
                idump( (_current_bid)(_current_ask) );

                // Make sure that at least one order was matched every time we enter the loop
                FC_ASSERT( _orders_filled != last_orders_filled, "We appear caught in an order matching loop" );
                last_orders_filled = _orders_filled;

                const asset bid_quantity_xts = _current_bid->get_quantity();
                const asset ask_quantity_xts = _current_ask->get_quantity();

                asset current_bid_balance = _current_bid->get_balance();

                // Initialize the market transaction
                market_transaction mtrx;
                mtrx.bid_owner = _current_bid->get_owner();
                mtrx.ask_owner = _current_ask->get_owner();
                // Always execute shorts at the center price
                mtrx.bid_price = (_current_bid->type != short_order) ? _current_bid->get_price() : _market_stat.center_price;
                mtrx.ask_price = _current_ask->get_price();
                mtrx.bid_type  = _current_bid->type;
                mtrx.ask_type  = _current_ask->type;

                if( _current_ask->type == cover_order && _current_bid->type == short_order )
                {
                   FC_ASSERT( quote_asset->is_market_issued() ); 

                   /** don't allow new shorts to execute unless there is a feed, all other
                    * trades are still valid. (we shouldn't stop the market)
                    */
                   if( !median_feed_price.valid() )
                   {
                      _current_bid.reset();
                      continue;
                   }

                   if( mtrx.ask_price < mtrx.bid_price ) // The call price has not been reached
                      break;

                   if( _current_bid->state.short_price_limit.valid() )
                   {
                      if( *_current_bid->state.short_price_limit < mtrx.ask_price )
                      {
                         _current_bid.reset();
                         continue; // skip shorts that are over the price limit.
                      }
                      mtrx.bid_price = *_current_bid->state.short_price_limit;
                   }

                   mtrx.ask_price = mtrx.bid_price;

                   // Bound collateral ratio
                   price collateral_rate = _current_bid->get_price();
                   if( collateral_rate > _market_stat.center_price )
                      collateral_rate = _market_stat.center_price;

                   const asset ask_quantity_usd = _current_ask->get_quote_quantity();
                   const asset short_quantity_usd = _current_bid->get_balance() / collateral_rate;

                   const asset trade_quantity_usd = std::min( short_quantity_usd, ask_quantity_usd );

                   mtrx.ask_received   = trade_quantity_usd;
                   mtrx.bid_paid       = mtrx.ask_received;

                   mtrx.ask_paid       = mtrx.ask_received * mtrx.ask_price;
                   mtrx.bid_received   = mtrx.ask_paid;

                   mtrx.bid_collateral = mtrx.bid_paid / collateral_rate;

                   // Handle rounding errors
                   if( (*mtrx.bid_collateral - _current_bid->get_balance()).amount < BTS_BLOCKCHAIN_PRECISION )
                       mtrx.bid_collateral = _current_bid->get_balance();

                   // If too little collateral at this price
                   if( *mtrx.bid_collateral < mtrx.ask_paid )
                   {
                       edump( (mtrx) );
                       _current_bid.reset();
                       continue;
                   }

                   pay_current_short( mtrx, *quote_asset );
                   pay_current_cover( mtrx, *quote_asset );

                   _market_stat.bid_depth -= mtrx.bid_collateral->amount;
                   _market_stat.ask_depth += mtrx.bid_collateral->amount;

                   order_did_execute = true;
                }
                else if( _current_ask->type == cover_order && _current_bid->type == bid_order )
                {
                   FC_ASSERT( quote_asset->is_market_issued()  );
                   if( mtrx.ask_price < mtrx.bid_price ) // The call price has not been reached
                      break;

                   /**
                    *  Don't allow margin calls to be executed too far below
                    *  the minimum ask, this could lead to an attack where someone
                    *  walks the whole book to steal the collateral.
                    */
                   if( mtrx.bid_price < _market_stat.minimum_ask() )
                   {
                      _current_ask.reset();
                      continue;
                   }

                   mtrx.ask_price = mtrx.bid_price;

                   const asset max_usd_purchase = asset( *_current_ask->collateral, _base_id ) * mtrx.bid_price;
                   asset usd_exchanged = std::min( current_bid_balance, max_usd_purchase );

                   // Bound quote asset amount exchanged
                   const asset required_usd_purchase = _current_ask->get_balance();
                   if( required_usd_purchase < usd_exchanged )
                      usd_exchanged = required_usd_purchase;

                   mtrx.ask_received = usd_exchanged;
                   mtrx.bid_paid     = mtrx.ask_received;

                   // Handle rounding errors
                   if( usd_exchanged == max_usd_purchase )
                      mtrx.ask_paid = asset(*_current_ask->collateral,_base_id);
                   else
                      mtrx.ask_paid = usd_exchanged * mtrx.bid_price;

                   mtrx.bid_received = mtrx.ask_paid;

                   pay_current_bid( mtrx, *quote_asset );
                   pay_current_cover( mtrx, *quote_asset );

                   // TODO: Do we need to decrease bid depth as well?
                   _market_stat.ask_depth -= mtrx.ask_paid.amount;

                   order_did_execute = true;
                }
                else if( _current_ask->type == ask_order && _current_bid->type == short_order )
                {
                   FC_ASSERT( quote_asset->is_market_issued() );

                   /** don't allow new shorts to execute unless there is a feed, all other
                    * trades are still valid.
                    */
                   if( !median_feed_price.valid() )
                   {
                      _current_bid.reset();
                      continue;
                   }

                   if( mtrx.bid_price < mtrx.ask_price ) // The ask price hasn't been reached
                      break;

                   if( _current_bid->state.short_price_limit.valid() )
                   {
                      if( *_current_bid->state.short_price_limit < mtrx.ask_price )
                      {
                         elog( "short price limit < bid price" );
                         _current_bid.reset();
                         continue; // skip shorts that are over the price limit.
                      }
                      mtrx.bid_price = *_current_bid->state.short_price_limit;
                   }

                   // Bound collateral ratio
                   price collateral_rate = _current_bid->get_price();
                   if( collateral_rate > _market_stat.center_price )
                      collateral_rate = _market_stat.center_price;

                   const asset ask_quantity_usd = _current_ask->get_quote_quantity();
                   const asset short_quantity_usd = _current_bid->get_balance() / collateral_rate;

                   const asset trade_quantity_usd = std::min( short_quantity_usd, ask_quantity_usd );

                   mtrx.ask_received   = trade_quantity_usd;
                   mtrx.bid_paid       = mtrx.ask_received;

                   mtrx.ask_paid       = mtrx.ask_received * mtrx.ask_price;
                   mtrx.bid_received   = mtrx.ask_paid;

                   mtrx.bid_collateral = mtrx.bid_paid / collateral_rate;

                   // Handle rounding errors
                   if( (*mtrx.bid_collateral - _current_bid->get_balance()).amount < BTS_BLOCKCHAIN_PRECISION )
                       mtrx.bid_collateral = _current_bid->get_balance();

                   // If too little collateral at this price
                   if( *mtrx.bid_collateral < mtrx.ask_paid )
                   {
                       edump( (mtrx) );
                       _current_bid.reset();
                       continue;
                   }

                   pay_current_short( mtrx, *quote_asset );
                   pay_current_ask( mtrx, *quote_asset );

                   _market_stat.bid_depth -= mtrx.bid_collateral->amount;
                   _market_stat.ask_depth += mtrx.bid_collateral->amount;

                   order_did_execute = true;
                }
                else if( _current_ask->type == ask_order && _current_bid->type == bid_order )
                {
                   if( mtrx.bid_price < mtrx.ask_price ) // The ask price hasn't been reached
                      break;

                   const asset quantity_xts = std::min( bid_quantity_xts, ask_quantity_xts );

                   // Everyone gets the price they asked for
                   mtrx.ask_received   = quantity_xts * mtrx.ask_price;
                   mtrx.bid_paid       = quantity_xts * mtrx.bid_price;

                   mtrx.ask_paid       = quantity_xts;
                   mtrx.bid_received   = quantity_xts;

                   // Handle rounding errors
                   if( quantity_xts == bid_quantity_xts )
                      mtrx.bid_paid = current_bid_balance;

                   mtrx.fees_collected = mtrx.bid_paid - mtrx.ask_received;

                   pay_current_bid( mtrx, *quote_asset );
                   pay_current_ask( mtrx, *base_asset );

                   // TODO: Do we need to decrease bid depth as well?
                   _market_stat.ask_depth -= mtrx.ask_paid.amount;

                   order_did_execute = true;
                }

                push_market_transaction( mtrx );

                if( mtrx.ask_received.asset_id == 0 )
                  trading_volume += mtrx.ask_received;
                else if( mtrx.bid_received.asset_id == 0 )
                  trading_volume += mtrx.bid_received;
                if( opening_price == price() )
                  opening_price = mtrx.bid_price;
                closing_price = mtrx.bid_price;

                if( mtrx.fees_collected.asset_id == base_asset->id )
                    base_asset->collected_fees += mtrx.fees_collected.amount;
                else if( mtrx.fees_collected.asset_id == quote_asset->id )
                    quote_asset->collected_fees += mtrx.fees_collected.amount;
             } // while( next bid && next ask )

             // update any fees collected
             _pending_state->store_asset_record( *quote_asset );
             _pending_state->store_asset_record( *base_asset );

             _market_stat.last_error.reset();

             // Force at least one center price update every 6 blocks
             order_did_execute |= (pending_block_num % 6) == 0;

             if( _current_bid && _current_ask && order_did_execute )
             {
                if( median_feed_price.valid() )
                {
                   _market_stat.center_price = *median_feed_price;
                }
                else if( _current_bid->type != short_order ) // we cannot use short prices for this
                {
                   _market_stat.center_price.ratio *= (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR-1);

                   const price max_bid = _market_stat.maximum_bid();

                   // limit the maximum movement rate of the price.
                   if( _current_bid->get_price() < min_cover_ask )
                      _market_stat.center_price.ratio += min_cover_ask.ratio;
                   else if( _current_bid->get_price() > max_bid )
                      _market_stat.center_price.ratio += max_bid.ratio;
                   else
                      _market_stat.center_price.ratio += _current_bid->get_price().ratio;

                   if( _current_ask->get_price() < min_cover_ask )
                      _market_stat.center_price.ratio += min_cover_ask.ratio;
                   else if( _current_ask->get_price() > max_bid )
                      _market_stat.center_price.ratio += max_bid.ratio;
                   else
                      _market_stat.center_price.ratio += _current_ask->get_price().ratio;

                   _market_stat.center_price.ratio /= (BTS_BLOCKCHAIN_BLOCKS_PER_HOUR+1);
                }
             }

             // Update market status and market history
             _pending_state->store_market_status( _market_stat );
             update_market_history( trading_volume, opening_price, closing_price, _market_stat, timestamp );

             wlog( "done matching orders" );
             idump( (_current_bid)(_current_ask) );

             _pending_state->apply_changes();
        }
        catch( const fc::exception& e )
        {
           wlog( "error executing market ${quote} / ${base}\n ${e}", ("quote",quote_id)("base",base_id)("e",e.to_detail_string()) );
           auto market_state = _prior_state->get_market_status( quote_id, base_id );
           if( !market_state )
              market_state = market_status( quote_id, base_id, 0, 0 );
           market_state->last_error = e;
           _prior_state->store_market_status( *market_state );
        }
      } // execute(...)