Skip to content

Commit

Permalink
Markets configuration is now automatic
Browse files Browse the repository at this point in the history
it fetches it through the API of every exchange
  • Loading branch information
Camille committed Dec 14, 2019
1 parent 840906b commit ed559fd
Show file tree
Hide file tree
Showing 42 changed files with 12,606 additions and 333 deletions.
35 changes: 8 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Run Arke with this command
```yaml
log_level: INFO
accounts:
- id: 1
- id: example-account1
driver: rubykube
debug: false
host: "https://example.com"
Expand All @@ -25,18 +25,17 @@ accounts:
secret: ""
delay: 0.75

- id: 2
- id: example-account2
driver: rubykube
debug: false
host: "https://example.com"
ws: "wss://example.com"
key: ""
secret: ""
delay: 0.01
delay: 1

- id: 3
- id: binance-account
driver: binance
host: "api.binance.com"
key: ""
secret: ""
delay: 1
Expand All @@ -59,24 +58,14 @@ strategies:
min_order_back_amount: 0.0002

target:
account_id: 1
account_id: example-account1
market:
id: BTCUSDT
base: btc
quote: usdt
base_precision: 4
quote_precision: 4
min_ask_amount: 0.015
min_bid_amount: 0.015

sources:
- account_id: 3
- account_id: binance-account
market:
id: BTCUSDT
base: BTC
quote: USDT
base_precision: 8
quote_precision: 8

- id: BTCUSDT-micro
type: microtrades
Expand All @@ -92,21 +81,17 @@ strategies:
max_price: 230

target:
account_id: 2
account_id: example-account2
market:
id: BTCUSDT
base: btc
quote: usdt
base_precision: 4
quote_precision: 4

```

#### Account config

| Field | Description |
| -------- | ------------------------------------------------------------ |
| `id` | ID identifying the account (have to be unique) |
| `id` | ID identifying the account (must be unique) |
| `driver` | Name of exchange driver (supported values are: `rubykube`, `binance`, `bitfinex`) |
| `debug` | Flag to extend logs verbosity, valid values are: `true` or `false` |
| `host` | Base URL of the exchange API |
Expand Down Expand Up @@ -227,10 +212,6 @@ The spread applied on circuitbraker strategy should be lower than the spead used
| Field | Description |
| ----------------------- | ------------------------------------------------------------ |
| `id` | ID of market |
| `base` | Base currency of market |
| `quote` | Quote currency of market |
| `base_precision` | Base precision of market |
| `quote_precision` | Quote precision of market |



Expand Down
21 changes: 21 additions & 0 deletions bin/luno-fetch-markets-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/ruby
# frozen_string_literal: true

require "pp"
require "json"
require "faraday"

pairs = %w[XBTEUR XBTIDR ETHNGN ETHZAR XBTMYR XBTNGN ETHXBT XBTZAR XBTZMW XBTSGD XBTUGX BCHXBT]
config = {}

pairs.each do |pair|
raise "Can't deduce base/quote from #{pair}" if pair.size != 6

warn "fetching #{pair}"
response = Faraday.get "https://www.luno.com/ajax/1/market_params?pair=#{pair}"
config[pair] = JSON.parse(response.body)
config[pair]["base_unit"] = pair[0..2]
config[pair]["quote_unit"] = pair[3..5]
end

pp config
36 changes: 10 additions & 26 deletions config/strategies.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
log_level: DEBUG
log_level: INFO

accounts:
- id: demo
driver: rubykube
debug: false
host: "https://demo.openware.com"
ws: "wss://demo.openware.com"
key: "353886962a3b3a79"
secret: "36827b03603d94496518390737a30562"
delay: 3
key: "df6330db624fcc67"
secret: "c2ac593fd25281a4cbf9df6f88d700a0"
delay: 1

- id: bitfaker
driver: bitfaker
Expand All @@ -27,11 +27,11 @@ accounts:
delay: 1

strategies:
- id: copy-FTHUSD
- id: copy-ETHUSD
type: copy
debug: true
enabled: true
period: 5
period: 30
params:
spread_bids: 0.003
spread_asks: 0.003
Expand All @@ -44,21 +44,12 @@ strategies:
target:
account_id: demo
market:
id: fthusd
base: fth
quote: usd
base_precision: 2
quote_precision: 2
min_bid_amount: 0.01
min_ask_amount: 0.01
- id: ethusd

sources:
- account_id: binance
market:
id: ETHUSDT
base: ETH
quote: USDT
min_bid_amount: 0.01
min_ask_amount: 0.01
- id: ETHUSDT

- id: microtrades-FTHUSD
type: microtrades
Expand All @@ -72,11 +63,4 @@ strategies:
max_amount: 0.30
target:
account_id: demo
market:
id: fthusd
base: fth
quote: usd
base_precision: 2
quote_precision: 2
min_ask_amount: 0.01
min_bid_amount: 0.01
market: { id: ethusd }
6 changes: 3 additions & 3 deletions lib/arke/action_executor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ def schedule(action)
execute do
order = action.params[:order]
logger.info { "ACCOUNT:#{id} #{CREATING} #{colored_side(order.side)} order: #{action.params}" }
price = apply_precision(order.price, action.destination.quote_precision)
amount = apply_precision(order.amount, action.destination.base_precision.to_f,
order.side == :sell ? action.destination.min_ask_amount.to_f : action.destination.min_bid_amount.to_f)
price = apply_precision(order.price, action.destination.price_precision)
amount = apply_precision(order.amount, action.destination.amount_precision.to_f,
order.side == :sell ? action.destination.min_amount.to_f : action.destination.min_amount.to_f)
begin
order = Arke::Order.new(order.market, price, amount, order.side)
action.destination.account.create_order(order)
Expand Down
4 changes: 4 additions & 0 deletions lib/arke/exchange/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ def balance(currency)
@balances.find {|b| b["currency"].casecmp?(currency) }
end

def market_config(_market)
raise "#{self.class} doesn't support market_config"
end

def build_query(params)
params.keys.sort.map {|k| "#{Faraday::Utils.escape(k)}=#{Faraday::Utils.escape(params[k])}" }.join("&")
end
Expand Down
43 changes: 33 additions & 10 deletions lib/arke/exchange/binance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(opts)
@client = ::Binance::Client::REST.new(api_key: @api_key, secret_key: @secret, adapter: @adapter)
@min_notional = {}
@min_quantity = {}
@base_precision = {}
@amount_precision = {}
@markets = opts["markets"]
end

Expand Down Expand Up @@ -102,13 +102,13 @@ def markets

def get_amount(order)
min_notional = @min_notional[order.market] ||= get_min_notional(order.market)
base_precision = @base_precision[order.market] ||= get_base_precision(order.market)
amount_precision = @amount_precision[order.market] ||= get_amount_precision(order.market)
percentage = 0.2
notional = order.price * order.amount
if notional > min_notional
order.amount
elsif (min_notional * percentage) < notional
return (min_notional / order.price).ceil(base_precision)
return (min_notional / order.price).ceil(amount_precision)
else
raise "Amount of order too small"
end
Expand Down Expand Up @@ -155,7 +155,7 @@ def fetch_openorders(market)
end
end

def get_base_precision(market)
def get_amount_precision(market)
min_quantity = @min_quantity[market] ||= get_min_quantity(market)
return 0 if min_quantity >= 1

Expand All @@ -167,16 +167,39 @@ def get_base_precision(market)
n
end

def get_symbol_info(market)
@exchange_info ||= @client.exchange_info["symbols"]
@exchange_info.find {|s| s["symbol"] == market }
end

def get_symbol_filter(market, filter)
get_symbol_info(market)["filters"].find {|f| f["filterType"] == filter }
end

def get_min_quantity(market)
@client.exchange_info["symbols"]
.find {|s| s["symbol"] == market }["filters"]
.find {|f| f["filterType"] == "LOT_SIZE" }["minQty"].to_f
get_symbol_filter(market, "LOT_SIZE")["minQty"].to_f
end

def get_min_notional(market)
@client.exchange_info["symbols"]
.find {|s| s["symbol"] == market }["filters"]
.find {|f| f["filterType"] == "MIN_NOTIONAL" }["minNotional"].to_f
get_symbol_filter(market, "MIN_NOTIONAL")["minNotional"].to_f
end

def market_config(market)
info = get_symbol_info(market)
raise "#{market} not found" unless info

price_filter = get_symbol_filter(market, "PRICE_FILTER")

{
"id" => info.fetch("symbol"),
"base_unit" => info.fetch("baseAsset"),
"quote_unit" => info.fetch("quoteAsset"),
"min_price" => price_filter&.fetch("minPrice").to_f,
"max_price" => price_filter&.fetch("maxPrice").to_f,
"min_amount" => get_min_quantity(market),
"amount_precision" => info.fetch("baseAssetPrecision"),
"price_precision" => info.fetch("quotePrecision")
}
end
end
end
17 changes: 6 additions & 11 deletions lib/arke/exchange/bitfaker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,16 @@ def get_balances
]
end

def get_market_infos(_market)
def market_config(_market)
{
"id" => "BTCUSD",
"name" => "BTC/USD",
"base_unit" => "BTC",
"quote_unit" => "USD",
"ask_fee" => "0.0002",
"bid_fee" => "0.0002",
"min_price" => "0.0",
"max_price" => "0.0",
"min_amount" => "0.00001",
"min_price" => 0.0,
"max_price" => 0.0,
"min_amount" => 0.1,
"amount_precision" => 6,
"price_precision" => 6,
"state" => "enabled",
}
end

Expand Down Expand Up @@ -98,9 +94,8 @@ def ping; end
private

def add_order(order)
_id, price, amount = order
side = amount.negative? ? :sell : :buy
amount = amount.abs
_id, _price, amount = order
amount.abs
end
end
end
20 changes: 20 additions & 0 deletions lib/arke/exchange/bitfinex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ def markets
@connection.get("/v1/symbols").body
end

def symbols_details
@symbols_details ||= @connection.get("/v1/symbols_details").body
end

def market_config(market)
info = symbols_details&.find {|i| i["pair"].downcase == market.downcase }
raise "Pair #{market} not found" unless info

{
"id" => info.fetch("pair"),
"base_unit" => nil,
"quote_unit" => nil,
"min_price" => nil,
"max_price" => nil,
"min_amount" => info.fetch("minimum_order_size"),
"amount_precision" => 8,
"price_precision" => 8,
}
end

def new_trade(data, market)
amount = data[2]
pm_id = @platform_markets[market]
Expand Down
Loading

0 comments on commit ed559fd

Please sign in to comment.