/** * This method should not affect market execution or validation and * is for historical purposes only. */ void update_market_history( const asset& trading_volume, const price& opening_price, const price& closing_price, const omarket_status& market_stat, const fc::time_point_sec& timestamp ) { if( trading_volume.amount > 0 && get_next_bid() && get_next_ask() ) { market_history_key key(_quote_id, _base_id, market_history_key::each_block, _db_impl._head_block_header.timestamp); market_history_record new_record(_current_bid->get_price(), _current_ask->get_price(), opening_price, closing_price, trading_volume.amount); FC_ASSERT( market_stat ); new_record.recent_average_price = market_stat->center_price; //LevelDB iterators are dumb and don't support proper past-the-end semantics. auto last_key_itr = _db_impl._market_history_db.lower_bound(key); if( !last_key_itr.valid() ) last_key_itr = _db_impl._market_history_db.last(); else --last_key_itr; key.timestamp = timestamp; //Unless the previous record for this market is the same as ours... if( (!(last_key_itr.valid() && last_key_itr.key().quote_id == _quote_id && last_key_itr.key().base_id == _base_id && last_key_itr.key().granularity == market_history_key::each_block && last_key_itr.value() == new_record)) ) { //...add a new entry to the history table. _pending_state->market_history[key] = new_record; } fc::time_point_sec start_of_this_hour = timestamp - (timestamp.sec_since_epoch() % (60*60)); market_history_key old_key(_quote_id, _base_id, market_history_key::each_hour, start_of_this_hour); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); old_record.recent_average_price = new_record.recent_average_price; _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; fc::time_point_sec start_of_this_day = timestamp - (timestamp.sec_since_epoch() % (60*60*24)); old_key = market_history_key(_quote_id, _base_id, market_history_key::each_day, start_of_this_day); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); old_record.recent_average_price = new_record.recent_average_price; _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; } }
void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp ) { try { _quote_id = quote_id; _base_id = base_id; auto quote_asset = _pending_state->get_asset_record( _quote_id ); // DISABLE MARKET ISSUED ASSETS if( quote_asset->is_market_issued() ) return; // don't execute anything. // the order book is soreted 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 auto 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( next_pair ) ); _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( _short_itr.valid() ) --_short_itr; else _short_itr = _db_impl._short_db.last(); 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 consumed_bid_depth(0,base_id); asset consumed_ask_depth(0,base_id); asset usd_fees_collected(0,quote_id); asset trading_volume(0, base_id); omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id ); if( !market_stat.valid() ) { if( quote_asset->is_market_issued() ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); FC_ASSERT( market_stat.valid() ); } while( get_next_bid() && get_next_ask() ) { idump( (_current_bid)(_current_ask) ); price ask_price = _current_ask->get_price(); // this works for bids, asks, and shorts.... but in the case of a cover // the current ask can go lower than the call price in order to match // the bid.... if( _current_ask->type == cover_order ) { ask_price = std::min( _current_bid->get_price(), _current_ask->get_highest_cover_price() ); } if( _current_bid->get_price() < ask_price ) break; if( quote_asset->is_market_issued() ) { if( !market_stat || market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 || market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); } auto quantity = std::min( _current_bid->get_quantity(), _current_ask->get_quantity() ); auto usd_paid_by_bid = quantity * _current_bid->get_price(); auto usd_received_by_ask = quantity * _current_ask->get_price(); auto xts_paid_by_ask = quantity; auto xts_received_by_bid = quantity; consumed_bid_depth += quantity; consumed_ask_depth += quantity; if( _current_bid->type == short_order ) { usd_paid_by_bid = usd_received_by_ask; } if( _current_ask->type == cover_order ) { usd_received_by_ask = usd_paid_by_bid; } FC_ASSERT( usd_paid_by_bid.amount >= 0 ); FC_ASSERT( xts_paid_by_ask.amount >= 0 ); FC_ASSERT( usd_received_by_ask.amount >= 0 ); FC_ASSERT( xts_received_by_bid.amount >= 0 ); FC_ASSERT( usd_paid_by_bid >= usd_received_by_ask ); FC_ASSERT( xts_paid_by_ask >= xts_received_by_bid ); // sanity check to keep supply from growing without bound FC_ASSERT( usd_paid_by_bid < asset(quote_asset->maximum_share_supply,quote_id), "", ("usd_paid_by_bid",usd_paid_by_bid)("asset",quote_asset) ); usd_fees_collected += usd_paid_by_bid - usd_received_by_ask; idump( (usd_fees_collected)(xts_paid_by_ask)(xts_received_by_bid)(quantity) ); market_transaction mtrx; mtrx.bid_owner = _current_bid->get_owner(); mtrx.ask_owner = _current_ask->get_owner(); mtrx.bid_price = _current_bid->get_price(); mtrx.ask_price = ask_price; mtrx.bid_paid = usd_paid_by_bid; mtrx.bid_received = xts_received_by_bid; mtrx.ask_paid = xts_paid_by_ask; mtrx.ask_received = usd_received_by_ask; mtrx.bid_type = _current_bid->type; mtrx.fees_collected = xts_paid_by_ask - xts_received_by_bid; _market_transactions.push_back(mtrx); trading_volume += mtrx.bid_received; market_stat->ask_depth -= xts_paid_by_ask.amount; if( _current_ask->type == ask_order ) { /* rounding errors on price cause this not to go to 0 in some cases */ if( quantity == _current_ask->get_quantity() ) _current_ask->state.balance = 0; else _current_ask->state.balance -= xts_paid_by_ask.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), quote_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,quote_id), 0 ); ask_payout->balance += usd_received_by_ask.amount; ask_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _pending_state->store_ask_record( _current_ask->market_index, _current_ask->state ); } else if( _current_ask->type == cover_order ) { elog( "MATCHING COVER ORDER recv_usd: ${usd} paid_collat: ${c}", ("usd",usd_received_by_ask)("c",xts_paid_by_ask) ); wlog( "current ask: ${c}", ("c",_current_ask) ); // we are in the margin call range... _current_ask->state.balance -= usd_received_by_ask.amount; *(_current_ask->collateral) -= xts_paid_by_ask.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); FC_ASSERT( *_current_ask->collateral >= 0 ); if( _current_ask->state.balance == 0 ) // no more USD left { // send collateral home to mommy & daddy wlog( " collateral balance is now 0!" ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), base_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,base_id), 0 ); ask_payout->balance += (*_current_ask->collateral); ask_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _current_ask->collateral = 0; } wlog( "storing collateral ${c}", ("c",_current_ask) ); _pending_state->store_collateral_record( _current_ask->market_index, collateral_record( *_current_ask->collateral, _current_ask->state.balance ) ); } if( _current_bid->type == bid_order ) { _current_bid->state.balance -= usd_paid_by_bid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto bid_payout = _pending_state->get_balance_record( withdraw_condition( withdraw_with_signature(_current_bid->get_owner()), base_id ).get_address() ); if( !bid_payout ) bid_payout = balance_record( _current_bid->get_owner(), asset(0,base_id), 0 ); bid_payout->balance += xts_received_by_bid.amount; bid_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *bid_payout ); _pending_state->store_bid_record( _current_bid->market_index, _current_bid->state ); } else if( _current_bid->type == short_order ) { market_stat->bid_depth -= xts_received_by_bid.amount; // TODO: what if the amount paid is 0 for bid and ask due to rounding errors, // make sure this doesn't put us in an infinite loop. if( quantity == _current_bid->get_quantity() ) _current_bid->state.balance = 0; else _current_bid->state.balance -= xts_received_by_bid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto collateral = (xts_paid_by_ask + xts_received_by_bid).amount; auto cover_price = usd_received_by_ask / asset( (3*collateral)/4, base_id ); market_index_key cover_index( cover_price, _current_ask->get_owner() ); auto ocover_record = _pending_state->get_collateral_record( cover_index ); if( NOT ocover_record ) ocover_record = collateral_record(); ocover_record->collateral_balance += collateral; ocover_record->payoff_balance += usd_received_by_ask.amount; FC_ASSERT( ocover_record->payoff_balance >= 0 ); FC_ASSERT( ocover_record->collateral_balance >= 0 ); _pending_state->store_collateral_record( cover_index, *ocover_record ); _pending_state->store_short_record( _current_bid->market_index, _current_bid->state ); } } // while bid && ask if( quote_asset->is_market_issued() ) { if( !market_stat || market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 || market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); } _pending_state->store_market_status( *market_stat ); if( trading_volume.amount > 0 && get_next_bid() && get_next_ask() ) { market_history_key key(quote_id, base_id, market_history_key::each_block, _db_impl._head_block_header.timestamp); market_history_record new_record(_current_bid->get_price(), _current_ask->get_price(), trading_volume.amount); //LevelDB iterators are dumb and don't support proper past-the-end semantics. auto last_key_itr = _db_impl._market_history_db.lower_bound(key); if( !last_key_itr.valid() ) last_key_itr = _db_impl._market_history_db.last(); else --last_key_itr; key.timestamp = timestamp; //Unless the previous record for this market is the same as ours... if( (!(last_key_itr.valid() && last_key_itr.key().quote_id == quote_id && last_key_itr.key().base_id == base_id && last_key_itr.key().granularity == market_history_key::each_block && last_key_itr.value() == new_record)) ) { //...add a new entry to the history table. _pending_state->market_history[key] = new_record; } fc::time_point_sec start_of_this_hour = timestamp - (timestamp.sec_since_epoch() % (60*60)); market_history_key old_key(quote_id, base_id, market_history_key::each_hour, start_of_this_hour); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; fc::time_point_sec start_of_this_day = timestamp - (timestamp.sec_since_epoch() % (60*60*24)); old_key = market_history_key(quote_id, base_id, market_history_key::each_day, start_of_this_day); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; } auto market_state = _pending_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.reset(); _pending_state->store_market_status( *market_state ); wlog( "done matching orders" ); _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(...)
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(...)