Author Topic: Subsidizing Market Liquidity  (Read 74361 times)

0 Members and 1 Guest are viewing this topic.

Offline xeroc

  • Board Moderator
  • Hero Member
  • *****
  • Posts: 12922
  • ChainSquad GmbH
    • View Profile
    • ChainSquad GmbH
  • BitShares: xeroc
  • GitHub: xeroc
Yes, some progresses.
Check https://data.sparkfun.com/bitshares_usd_price for a feed_price only stream. Market snapshots stream would be something like that but with much more data, which can be used to calculate scores.
If I'm given a decided formula, I can also stream scores.
This looks great .. !!!

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
My idea can be considered that a new snapshot will be taken on every new block, so all unfilled orders will be tracked. For partially filled orders, we'll know when they were created, when they were partially filled, and when they disappeared.

Sounds good.. so this data will be streamed to a log file for processing/scoring by external scripts? It would be ideal to have multiple people running the same script and checking to make sure the numbers are in agreement. Your approach sounds like the most accurate way to get the data.
I'll stream data to somewhere on the Internet.

@roadscape , @abit

Hey guys, how soon do you think this can be completed?  @abit, have you been making progress?  Ronny from @ccedk said he would be interested in utilizing this to reward market makers in his UIAs.  And he will soon be launching some high profile assets suck as Lisk, Digix, and Synerio.  Not to mention, it is critical for us to start bootstrapping key fiat BitAssets.  What do you guys think?
Yes, some progresses.
Check https://data.sparkfun.com/bitshares_usd_price for a feed_price only stream. Market snapshots stream would be something like that but with much more data, which can be used to calculate scores.
If I'm given a decided formula, I can also stream scores.
BitShares committee member: abit
BitShares witness: in.abit

Offline tbone

  • Hero Member
  • *****
  • Posts: 632
    • View Profile
  • BitShares: tbone2
Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
My idea can be considered that a new snapshot will be taken on every new block, so all unfilled orders will be tracked. For partially filled orders, we'll know when they were created, when they were partially filled, and when they disappeared.

Sounds good.. so this data will be streamed to a log file for processing/scoring by external scripts? It would be ideal to have multiple people running the same script and checking to make sure the numbers are in agreement. Your approach sounds like the most accurate way to get the data.
I'll stream data to somewhere on the Internet.

@roadscape , @abit

Hey guys, how soon do you think this can be completed?  @abit, have you been making progress?  Ronny from @ccedk said he would be interested in utilizing this to reward market makers in his UIAs.  And he will soon be launching some high profile assets suck as Lisk, Digix, and Synerio.  Not to mention, it is critical for us to start bootstrapping key fiat BitAssets.  What do you guys think?

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
My idea can be considered that a new snapshot will be taken on every new block, so all unfilled orders will be tracked. For partially filled orders, we'll know when they were created, when they were partially filled, and when they disappeared.

Sounds good.. so this data will be streamed to a log file for processing/scoring by external scripts? It would be ideal to have multiple people running the same script and checking to make sure the numbers are in agreement. Your approach sounds like the most accurate way to get the data.
I'll stream data to somewhere on the Internet.
BitShares committee member: abit
BitShares witness: in.abit

Offline roadscape

Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
My idea can be considered that a new snapshot will be taken on every new block, so all unfilled orders will be tracked. For partially filled orders, we'll know when they were created, when they were partially filled, and when they disappeared.

Sounds good.. so this data will be streamed to a log file for processing/scoring by external scripts? It would be ideal to have multiple people running the same script and checking to make sure the numbers are in agreement. Your approach sounds like the most accurate way to get the data.
http://cryptofresh.com  |  witness: roadscape

Offline tbone

  • Hero Member
  • *****
  • Posts: 632
    • View Profile
  • BitShares: tbone2
Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
My idea can be considered that a new snapshot will be taken on every new block, so all unfilled orders will be tracked. For partially filled orders, we'll know when they were created, when they were partially filled, and when they disappeared.

If it isn't too resource intensive, snapshot on every block sounds great.

Offline tbone

  • Hero Member
  • *****
  • Posts: 632
    • View Profile
  • BitShares: tbone2
Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?

Since we're trying to incentivize orders placed, not orders filled, I don't think we need to concern ourselves with partial fills except to the extent that a partial fill reduces the number of shares an MM has on the order book and is getting credit for at that moment.  So if an MM reduces the size of their order, it should have the same impact on the scoring as a partial fill. 

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
My idea can be considered that a new snapshot will be taken on every new block, so all unfilled orders will be tracked. For partially filled orders, we'll know when they were created, when they were partially filled, and when they disappeared.
BitShares committee member: abit
BitShares witness: in.abit

Offline roadscape

Discussion also here: https://github.com/cryptonomex/graphene/issues/643

At this point I'm thinking @abit's solution would indeed work great as long as he can implement this detail:

OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

So each snapshot really needs to be a window of time (say 15mins) that contains every order that existed during that time PLUS its placement time and/or fill time. Is this what you were thinking @abit? Any idea how partially filled orders are handled with this approach?
http://cryptofresh.com  |  witness: roadscape

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore

@abit, that would be great if this could be a plugin for the node. It will be much more efficient and accurate than doing this thru API.

@roadscape: Many orders will be on the books for much less than 10 minutes.  So I think sampling every 10 minutes will yield somewhat arbitrary results.  On the other hand, I imagine constant monitoring would be too resource intensive?  If so, how about sampling every 1 minute?  I don't think it would yield perfect results, but probably more than good enough, and I'm guessing without being too unreasonably resource intensive.  Thoughts?  @abits, can you comment on this as well?

@tbone It could probably be done every minute. If we have many quickly placed/filled orders at high volumes then it would not make sense to use the sampling approach tho. But I figured most the orders getting rewarded would be bigger walls that don't move quickly. But there are workarounds, I just wanted to explore the limits of that approach and get feedback from traders.


Your middle ground is a good idea, the main issue--I don't think it's possible to get all this info solely via RPC calls; it would require extra processing. This is doable but at that point I'd weigh it against the continuous approach.
However @abit's solution (processing data straight in the node) is probably the most proper way to accomplish this.
@roadscape too be more efficient, at first I'll write a plugin which provides basic snapshot data only via RPC (E.G. what orders exist at a given time and what the feed price is, or data sets for a given time range), then you and/or others can do more processing (easier than querying via current APIs).
Implementing all processing logic in the plugin sounds not a good idea for me.
BitShares committee member: abit
BitShares witness: in.abit

Offline roadscape

@roadscape I prefer to calculate with full data rather than with sample data. I'm going to write a snapshot plugin.

Data structure of one market pair (for example BTS/USD):
* a calculation period = snapshots
* a snapshot = time stamp, feed price, ask orders, bid orders
* a order = direction(ask/bid), price, volume, owner, create_time

Calculations:
[Note: the algorithm below is based on feed price, but not on "best prices"]
* at the beginning of a calculation period, take a snapshot, set everyone's score = 0
* Every time (after a new block is produced) when a new order is created or cancelled, or feed price is updated, or reached the end of a calculation period, take a new snapshot.
 -> let LT= last snapshot time
     let T = current snapshot time
     let LFP = feed price of last snapshot
 -> for each order,
     let LV=volume of last snapshot,
     calculate LV' = function1 (direction, price, LFP, LV), to filter out unqualified orders
     calculate new score gained by this order after last snapshot
          NOS = function2 (direction,LV', LT, T, create_time, price, LFP)
          note: create_time may be needed here to judge whether this order is qualified.
 -> for each account(owner) on each side,
     let LAS = score on last snapshot
     calculate TLV = sum(LV'), total volume of qualified orders at last snapshot
     calculate ELV = function3(TLV), to filter out unqualified account if total volume is too low
     calculate TNS = sum(NOS), total new score gained
     calculate ENS = function4(TNS,ELV), to filter out too low total score/volume, and perhaps set a cap on new score
     calculate AS = LAS + ENS, final score at this snapshot
* at the end of a calculation period, for each side, we have a set of (account, score), so we can calculate the final rewards.

//Update: I'm going to write a plugin to provide snapshot data, so you guys can use it to do whatever calculation as you like.

@abit, that would be great if this could be a plugin for the node. It will be much more efficient and accurate than doing this thru API.

@roadscape: Many orders will be on the books for much less than 10 minutes.  So I think sampling every 10 minutes will yield somewhat arbitrary results.  On the other hand, I imagine constant monitoring would be too resource intensive?  If so, how about sampling every 1 minute?  I don't think it would yield perfect results, but probably more than good enough, and I'm guessing without being too unreasonably resource intensive.  Thoughts?  @abits, can you comment on this as well?

@tbone It could probably be done every minute. If we have many quickly placed/filled orders at high volumes then it would not make sense to use the sampling approach tho. But I figured most the orders getting rewarded would be bigger walls that don't move quickly. But there are workarounds, I just wanted to explore the limits of that approach and get feedback from traders.

tonyk Your proposed changes make sense, but continuous monitoring is much more complex than sampling*. What does it capture that sampling can't? And what if samples were e.g. 15 mins apart?

@roadscape
OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

My question is, what scenario are you trying to avoid (or create) by doing this?

If your order is on the books for 120 mins, and is *completely* filled at minute 125, you would not get credit for those last 5 minutes (assuming 10-minute snapshot interval). To me this doesn't seem like a problem.

If you expect orders to be on the books for less than 10 minutes at a time, I could see why we would need to be tracking this more detailed order activity.

My original line of thinking was a simple "sharedrop" of points onto order book participants at a regular interval.

My assumptions for MM subsidies:
1) The actual market activity doesn't matter nearly as much as creating a nicely-shaped order book.
2) 'Sampling' the order book every ~10 minutes is at least 95% as accurate as analyzing continuous data.
Obviously (I thought), I am trying to avoid orders being on the order book for 30% -99% of the time between sample taking, and such orders getting no credit at all. To say nothing about such orders being filled first, means they had the best prices of all orders!!!
The less the time between sample taking the less an issue this becomes, but your original proposal was every 1 h. (way too long in my view). The smaller the time between each sample taking the less an issue it is. (so if you cut it to 3-5-7 min, this is a one way to do it)
Yet, again the proposed solution will yield ' near perfect'* results even if you sample far less frequently... and the computations involved seem to take not too much recourses.

* It is not perfect, cause if you sample once every hour, you should also credit the "placed and subsequently cancelled" orders, meeting all other criteria, in that time frame.

Your middle ground is a good idea, the main issue--I don't think it's possible to get all this info solely via RPC calls; it would require extra processing. This is doable but at that point I'd weigh it against the continuous approach.
However @abit's solution (processing data straight in the node) is probably the most proper way to accomplish this.
http://cryptofresh.com  |  witness: roadscape

Offline abit

  • Committee member
  • Hero Member
  • *
  • Posts: 4664
    • View Profile
    • Abit's Hive Blog
  • BitShares: abit
  • GitHub: abitmore
Do we need to subsidize force settlement orders and/or call orders (collateral holders)?
BitShares committee member: abit
BitShares witness: in.abit

Offline tonyk

  • Hero Member
  • *****
  • Posts: 3308
    • View Profile
tonyk Your proposed changes make sense, but continuous monitoring is much more complex than sampling*. What does it capture that sampling can't? And what if samples were e.g. 15 mins apart?

@roadscape
OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

My question is, what scenario are you trying to avoid (or create) by doing this?

If your order is on the books for 120 mins, and is *completely* filled at minute 125, you would not get credit for those last 5 minutes (assuming 10-minute snapshot interval). To me this doesn't seem like a problem.

If you expect orders to be on the books for less than 10 minutes at a time, I could see why we would need to be tracking this more detailed order activity.

My original line of thinking was a simple "sharedrop" of points onto order book participants at a regular interval.

My assumptions for MM subsidies:
1) The actual market activity doesn't matter nearly as much as creating a nicely-shaped order book.
2) 'Sampling' the order book every ~10 minutes is at least 95% as accurate as analyzing continuous data.
Obviously (I thought), I am trying to avoid orders being on the order book for 30% -99% of the time between sample taking, and such orders getting no credit at all. To say nothing about such orders being filled first, means they had the best prices of all orders!!!
The less the time between sample taking the less an issue this becomes, but your original proposal was every 1 h. (way too long in my view). The smaller the time between each sample taking the less an issue it is. (so if you cut it to 3-5-7 min, this is a one way to do it)
Yet, again the proposed solution will yield ' near perfect'* results even if you sample far less frequently... and the computations involved seem to take not too much recourses.

* It is not perfect, cause if you sample once every hour, you should also credit the "placed and subsequently cancelled" orders, meeting all other criteria, in that time frame.
Lack of arbitrage is the problem, isn't it. And this 'should' solves it.

Offline tbone

  • Hero Member
  • *****
  • Posts: 632
    • View Profile
  • BitShares: tbone2
@tbone @cylonmaker2053 currently the scoring bonus is linear: 100% bonus @ the midpoint, and 0% bonus at 5% off. This could be scaled to a wider range, and we could also use a curve instead of a line (creating a "long tail") for the bonus.

yes, wider margin (like 20%+) would be the most important change. linear is fine as long as the margin is wider, but curved with weights trailing off towards the tails would be best.

I agree with these points.

Offline tbone

  • Hero Member
  • *****
  • Posts: 632
    • View Profile
  • BitShares: tbone2
@abit if you think any part of this can be done from within graphene, that's great. I've been looking at it from an API perspective.

@tbone @cylonmaker2053 currently the scoring bonus is linear: 100% bonus @ the midpoint, and 0% bonus at 5% off. This could be scaled to a wider range, and we could also use a curve instead of a line (creating a "long tail") for the bonus.

tonyk Your proposed changes make sense, but continuous monitoring is much more complex than sampling*. What does it capture that sampling can't? And what if samples were e.g. 15 mins apart?

@roadscape
OK, how about a middle ground - taking the snapshot every 10 (20, 30 whatever) minutes BUT also reading the filled orders in that period and using them for the calculation[effectively adding them to the orderbook like they were not filled]?
We can do 2 diff things - either credit them for the whole time period or really check when they were placed and  filled and credit them with the correct real time they were on the book.

####
thisTimeIntervalStart = now() - 10 min
For each filled order in time [now, thisTimeIntervalStart]
      T = OrderFillTime - max(OrderPlacementTime,  thisTimeIntervalStart)
       order_total = size of the Filled Order
####

My question is, what scenario are you trying to avoid (or create) by doing this?

If your order is on the books for 120 mins, and is *completely* filled at minute 125, you would not get credit for those last 5 minutes (assuming 10-minute snapshot interval). To me this doesn't seem like a problem.

If you expect orders to be on the books for less than 10 minutes at a time, I could see why we would need to be tracking this more detailed order activity.

My original line of thinking was a simple "sharedrop" of points onto order book participants at a regular interval.

My assumptions for MM subsidies:
1) The actual market activity doesn't matter nearly as much as creating a nicely-shaped order book.
2) 'Sampling' the order book every ~10 minutes is at least 95% as accurate as analyzing continuous data.

@roadscape: Many orders will be on the books for much less than 10 minutes.  So I think sampling every 10 minutes will yield somewhat arbitrary results.  On the other hand, I imagine constant monitoring would be too resource intensive?  If so, how about sampling every 1 minute?  I don't think it would yield perfect results, but probably more than good enough, and I'm guessing without being too unreasonably resource intensive.  Thoughts?  @abits, can you comment on this as well?