Ember handling of exchange commissions
Overview
Recent survey of Exchange APIs found that less than 10% of crypto exchanges provide amount of fee charged with their trade notification messages. Similarly, only about half of exchanges provide information on Maker vs Taker flag on fill notifications. To overcome this problem Deltix come up with a method of estimating fees based on schedules published by each exchange.
This document describes a component used by Deltix to estimate commission from different exchanges.
Background
Kinds of Fee Schedule
- Flat (BTCBox, Bittrex)
- Different fee for Makers and Takers (Deribit)
- Different fee for each contract with a difference for Makers and Takers (Cryptofacilities, Huobi)
- Tiered fee structure where fee depends on rolling 30-day trading volume and is different for Makers and Takers. This is the most popular schedule (Coinbase Pro, Kraken, etc).
Additional variations
- On some exchanges (Korbit, SeedCX) makers receive rebates (rather than pay fees).
- On some exchanges fee may depend on instrument type (e.g. spot vs futures) or type of coins (stable vs normal).
Loyalty coins
Some exchanges (Cuoine/Liquid, Binance) reduce fees or promote clients into higher trading volume tier based on the amount of exchange coins held or used to pay commission.
Fee Rounding
All exchanges round fees up (up to two decimal points). For example; a fee of 0.111 will be charged as 0.12.
Design Considerations
Deltix designed customizable Commission Rules engine that has the following approach to manage unknowns (imperfections of exchange APIs):
- Client should have an ability to override official schedule fee. For example, some OTC desks that primarily use SMART order routing to seek liquidity predominantly trade as market takers. While other deployments (such as Market Maker may use “post only” maker orders). Such knowledge can be captured in a custom commission schedule.
- Some clients negotiate special fees with exchanges.
- If maker/taker side of the deal is unknown, assume taker (higher commission).
- If 30-days trading volume is unknown, assume the lowest (higher commission).
Commission Rule Engine
Each trading connector can define optional fee schedule in Execution Server configuration file (ember.conf). Let’s consider fee schedule of COINBASE exchange as an example:
Priciing Tier | Taker Fee | Maker Fee |
---|---|---|
<$100K | 0.25% | 0.15% |
10K-1M | 0.20% | 0.10% |
1-10M | 0.18% | 0.08% |
10-50M | 0.15% | 0.05% |
50-100M | 0.10% | 0.0% |
Here is how this fee schedule is configured in ember.conf
:
COINBASE: ${template.connector.COINBASE} {
...
feeSchedule {
fees: [
{ tradingVolume = 0, takerFee = 0.25, makerFee = 0.15 }
{ tradingVolume = 100000, takerFee = 0.20, makerFee = 0.10 }
{ tradingVolume = 1000000, takerFee = 0.18, makerFee = 0.08 }
{ tradingVolume = 10000000, takerFee = 0.15, makerFee = 0.50 }
{ tradingVolume = 50000000, takerFee = 0.10, makerFee = 0.00 }
// etc ...
]
}
}
Each fee schedule entry can use the following attributes:
tradingVolume
- Optional. 30 days trading volume. Rules Engine will rely on TimeBase stream “tradingVolumes”. Stream can be updated regularly using simple.
Trading volume currency usually will be USD, but may be exchange-dependent (e.g. KRW for Korbit).symbol
- Optional. Symbol (or instrument), such as BTCUSD.currency
- Optional. For example, BTC. Currency and Symbol are mutually exclusive attributes.takerFee
- Required. Fee that takers pay, in percentage from total fill value. Can be zero.makerFee
- Required. Fee that makers pay, in percentage from total fill value. Can be zero or negative (in case of exchange rebate).
Inverse Futures
Normally commission amount is computed as production of
Fee = Trade Quantity * Average Trade Price * FeeRate
For inverse futures fee computation uses slightly different formula:
Fee = ( Trade Quantity / Average Trade Price ) * FeeRate
To mark fee schedule as inverse use special flag (default is false):
feeSchedule {
isInverse = true
fees: [
{tradingVolume = 0, takerFee = 0.075, makerFee = 0}
]
}
More examples
- Flat fee:
feeSchedule {
fees: [
{ takerFee = 0.25, makerFee = 0.25 }
]
}
- Different Maker/Taker fee:
feeSchedule {
fees: [
{ takerFee = 0.15, makerFee = 0.25 }
]
}
- Currency-dependent fee:
feeSchedule {
fees: [
{ currency = BTC, takerFee = 0.25, makerFee = 0.25 }
{ currency = ETH, takerFee = 0.25, makerFee = 0.25 }
{ currency = LTC, takerFee = 0.25, makerFee = 0.25 }
]
}
- Symbol and trading volume
feeSchedule {
fees: [
{ symbol = BTCUSD, tradingVolume = 0, takerFee = 0.25, makerFee = 0.15 }
{ symbol = BTCUSD, tradingVolume = 100000, takerFee = 0.20, makerFee = 0.10 }
{ symbol = BTCUSD, tradingVolume = 1000000, takerFee = 0.18, makerFee = 0.08 }
{ symbol = BTCUSD, tradingVolume = 10000000, takerFee = 0.15, makerFee = 0.50 }
{ symbol = BTCUSD, tradingVolume = 50000000, takerFee = 0.10, makerFee = 0.00 }
{ symbol = LTCUSD, tradingVolume = 0, takerFee = 0.25, makerFee = 0.15 }
{ symbol = LTCUSD, tradingVolume = 100000, takerFee = 0.20, makerFee = 0.10 }
{ symbol = LTCUSD, tradingVolume = 1000000, takerFee = 0.18, makerFee = 0.08 }
{ symbol = LTCUSD, tradingVolume = 10000000, takerFee = 0.15, makerFee = 0.50 }
{ symbol = LTCUSD, tradingVolume = 50000000, takerFee = 0.10, makerFee = 0.00 }
]
}
Daily Trading Volume
Ember Commission Engine expects trading volume data in the following format in a TimeBase stream called “tradingVolumes”. Here is a sample. Complete schema of the stream can be found in Appendix A.
Timestamp | Symbol(Exchange) | Volume |
---|---|---|
6/15/2019 00:00:00.000 | COINBASE | 132142.03 |
6/16/2019 00:00:00.000 | COINBASE | 100.00 |
6/17/2019 00:00:00.000 | COINBASE | 230598.76 |
... | ... | ... |
- Timestamp - will identify the date of the trading volume record. Trading days begin at midnight UTC. Multiple records that belong to the same day will have their reported volumes combined.
- Symbol - this field will be populated with exchange ID (or destination ID as it is defined in ember.conf).
- Volume - trading volume reported on that date. For exchanges that use USD currency for trade volume discounts units are USD (Otherwise exchange-specific currency, for example BINANCE uses BTC).
- Transactions made on books quoted in USD, e.g. BTC-USD, are counted as the total USD amount of each filled order.
- Transactions made on non-USD books are converted to USD based on the most recent fill price on the respective book. For example, a purchase of 1 ETH on the ETH-BTC book will be immediately converted to a USD equivalent based on the most recent fill price on the ETH-USD book.
When loading trading volume from CSV file using ManualVolumeReporting tool this column uses yyyy-MM-dd HH:mm:ss
timestamp format in UTC timezone. For example, 2019-06-19T17:00:00
As in other areas of Deltix products exchange must use `ALPHANUMERIC(10)`` format (limited to upper-case English letters, digits and punctuation symbols, up to 10 characters long).
Multiple trading systems must report their trading volumes into multiple streams. Commission Engine will allow defining additional trading volume streams.
When calculating commission discounts Ember will total volumes reported for the last 30 calendar days.
Multiple sources of trading volume
You can setup multiple publishers of trading volume, each in different stream. System will use combined trading volume grouped by each exchange.
Configuration
By default, Ember looks for commissions in “tradingVolumes” stream. Additional streams can be configured as shown:
feeSchedule.settings.streams = [ tradingVolumes, tradingVolumesMarketMaker ]
Trading volume is recalculated once a day at midnight UTC. You can override this using the following settings:
feeSchedule {
settings {
calculationTimeOfDay = "23:59:59.999"
timezone = America/New_York
}
}
Trading Volume timestamps and time zone
Trading volume computation routine uses the following approach.
- Routine uses the last midnight in UTC timezone and subtracts 30 calendar days from it.
- It loads all messages from trading volume stream starting from this timestamp (inclusive) till end of stream.
- Messages are grouped by exchange and volume is totaled.
Logging
Ember logs commission information for each exchange on start up. Here is a sample:
Calculated 30-day trading volume for COINBASE is 30000700. Commissions: taker=0.0015 maker=0.005
Trading volume is unknown for KRAKEN. Commissions: taker=0.0026 maker=0.0016
Manual Tool (BETA)
Deltix provides simple command line tool to load trading volume from CSV file into TimeBase.
Input format sample is similar to described above. Here is a working sample:
;Timestamp, Exchange, Volume
2019-06-16 17:00:00, COINBASE, 100
2019-06-17 17:00:00, COINBASE, 200
2019-06-18 17:00:00, COINBASE, 300
Tool takes two arguments: TimeBase URI and Input file name. Example:
deltix.ember.app.fee.tb.ManualVolumeReporter dxtick://localhost:8011 tradeVolume.csv
records are loaded into TimeBase with TRUNCATE append mode. This means re-loading already loaded records may introduce issues (Stream truncated every time when loader writes messages earlier that last). You should avoid re-loading records if you don’t know what you are doing.
Daily Volume Publisher Tool
Deltix provides command line tool to compute trading volume from trading events in journal and load it into TimeBase. The name of the script running the tool is dv-publisher
. It takes these parameters:
Usage: dv-publisher -marketStreams <arg> [-timebase <arg>] [-volumeStream <arg>]
[-currency <arg>] [-maxDays <arg>] [-dryRun]
-marketStreams <arg> Comma-separated list of market streams that provide
data about market prices of currencies on exchanges
-timebase <arg> Timebase URL (default is dxtick://localhost:8011)
-volumeStream <arg> Volume stream where computed trading volume records
will be written (default is "tradingVolumes")
-currency <arg> Currency in which trading volume is computed
(default "USD")
-maxDays <arg> Maximum number of days for which trading volume
is computed (default is 30)
-dryRun Execute a dry run with trading volumes written
to the console only
Limitations
- Commissions will be recorded into Ember journal during fill processing time. Subsequent changes to fee schedule will not affect past commissions.
- Trading Volume computation may yield approximate results. For example, on some exchanges today’s recent trades are included in 30-day trading volume.
- Trading Volume computation won’t be able to factor external trades happening on the same account.
- Fee Schedule changes will require Ember restart.
- Support for fees based on asset class is not implemented.
Out of scope
- An ability to pay commissions with exchange loyalty coins to get reduced rate.
- Support past/future fee schedules
- Auction trade fees
Appendix: Trading Volume Stream Schema
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://xml.deltixlab.com/internal/quantserver" xmlns:ns2="http://xml.deltixlab.com/internal/quantserver/3.0">
<isFixed>true</isFixed>
<descriptors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:recordClass">
<ns2:name>deltix.ember.app.fee.tb.DailyTradingVolumeMessage</ns2:name>
<ns2:title />
<ns2:description />
<ns2:guid>a9fee77cc97d3mws2sov_3</ns2:guid>
<ns2:abstract>false</ns2:abstract>
<ns2:field xsi:type="ns2:nonStaticDataField">
<ns2:name>Source Sequence Number</ns2:name>
<ns2:title>Source Sequence Number</ns2:title>
<ns2:description />
<ns2:type xsi:type="ns2:INTEGER">
<ns2:encoding>INT64</ns2:encoding>
<ns2:nullable>true</ns2:nullable>
</ns2:type>
<ns2:pk>false</ns2:pk>
<ns2:displayIdentifier>false</ns2:displayIdentifier>
</ns2:field>
<ns2:field xsi:type="ns2:nonStaticDataField">
<ns2:name>Volume</ns2:name>
<ns2:title>Volume</ns2:title>
<ns2:description />
<ns2:type xsi:type="ns2:FLOAT">
<ns2:encoding>IEEE64</ns2:encoding>
<ns2:nullable>false</ns2:nullable>
</ns2:type>
<ns2:pk>false</ns2:pk>
<ns2:displayIdentifier>false</ns2:displayIdentifier>
</ns2:field>
</descriptors>
<selection>0</selection>
</schema>