Last active
August 2, 2018 16:05
-
-
Save redink/60559d86886f8923ad70262bf704eff4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Trade.MatchServer do | |
use GenServer | |
require Logger | |
@buy_queue :buy_queue | |
@sell_queue :sell_queue | |
@unhandled 0 | |
@before_generate_tx 1 | |
@after_generate_tx 2 | |
@created_new_order 3 | |
def start_link(_) do | |
GenServer.start_link(__MODULE__, [], name: __MODULE__) | |
end | |
def order_buy(%{price: buy_price, timestamp: timestamp} = order) do | |
:ets.insert(@buy_queue, {{buy_price, -timestamp, order}, @unhandled}) | |
end | |
def order_sell(%{price: sell_price, timestamp: timestamp} = order) do | |
:ets.insert(@sell_queue, {{sell_price, timestamp, order}, @unhandled}) | |
end | |
def init(_) do | |
# create buy queue and sell queue | |
:ets.new(@buy_queue, [:named_table, :ordered_set, :public]) | |
:ets.new(@sell_queue, [:named_table, :ordered_set, :public]) | |
Process.send_after(self(), :try_match, get_match_interval()) | |
{:ok, %{}} | |
end | |
def handle_info(:try_match, state) do | |
try_handle_match_order(last_buy_queue(), first_sell_queue()) | |
Process.send_after(self(), :try_match, get_match_interval()) | |
{:noreply, state} | |
end | |
defp get_match_interval do | |
Application.get_env(:trade, :match_server_interval, 10) | |
end | |
defp try_handle_match_order(:"$end_of_table", _), do: nil | |
defp try_handle_match_order(_, :"$end_of_table"), do: nil | |
defp try_handle_match_order({buy_price, _, _} = last_buy, {sell_price, _, _} = first_sell) | |
when buy_price >= sell_price do | |
handle_match_order(lookup_buy_order(last_buy), lookup_sell_order(first_sell)) | |
end | |
defp try_handle_match_order(_, _), do: nil | |
defp handle_match_order({last_buy, buy_tag}, {first_sell, sell_tag}) | |
when (buy_tag == @unhandled and sell_tag == @unhandled) or | |
(buy_tag == @before_generate_tx and sell_tag == @unhandled) or | |
(buy_tag == @unhandled and sell_tag == @before_generate_tx) or | |
(buy_tag == @before_generate_tx and sell_tag == @before_generate_tx) do | |
{_buy_price, _buy_time, %{amount: buy_amount} = buy_order} = last_buy | |
{_sell_price, _sell_time, %{amount: sell_amount} = sell_order} = first_sell | |
prepare_generate_tx(last_buy, first_sell) | |
# maybe tx had been generated, be careful duplicate tx | |
generate_tx(last_buy, first_sell) | |
generate_tx_done(last_buy, first_sell) | |
cond do | |
buy_amount == sell_amount -> nil | |
buy_amount > sell_amount -> order_buy(%{buy_order | amount: buy_amount - sell_amount}) | |
buy_amount < sell_amount -> order_sell(%{sell_order | amount: sell_amount - buy_amount}) | |
end | |
created_new_order(last_buy, first_sell) | |
delete_last_buy_queue(last_buy) | |
delete_first_sell_queue(first_sell) | |
end | |
defp handle_match_order({last_buy, buy_tag}, {first_sell, sell_tag}) | |
when buy_tag == @after_generate_tx or sell_tag == @after_generate_tx do | |
{_buy_price, _buy_time, %{amount: buy_amount} = buy_order} = last_buy | |
{_sell_price, _sell_time, %{amount: sell_amount} = sell_order} = first_sell | |
generate_tx_done(last_buy, first_sell) | |
cond do | |
buy_amount == sell_amount -> nil | |
buy_amount > sell_amount -> order_buy(%{buy_order | amount: buy_amount - sell_amount}) | |
buy_amount < sell_amount -> order_sell(%{sell_order | amount: sell_amount - buy_amount}) | |
end | |
created_new_order(last_buy, first_sell) | |
delete_last_buy_queue(last_buy) | |
delete_first_sell_queue(first_sell) | |
end | |
defp handle_match_order({last_buy, buy_tag}, {first_sell, sell_tag}) do | |
{_buy_price, _buy_time, %{amount: buy_amount} = buy_order} = last_buy | |
{_sell_price, _sell_time, %{amount: sell_amount} = sell_order} = first_sell | |
cond do | |
buy_amount == sell_amount -> | |
nil | |
buy_amount > sell_amount -> | |
if buy_tag == @created_new_order do | |
nil | |
else | |
order_buy(%{buy_order | amount: buy_amount - sell_amount}) | |
end | |
buy_amount < sell_amount -> | |
if sell_tag == @created_new_order do | |
nil | |
else | |
order_sell(%{sell_order | amount: sell_amount - buy_amount}) | |
end | |
end | |
created_new_order(last_buy, first_sell) | |
delete_last_buy_queue(last_buy) | |
delete_first_sell_queue(first_sell) | |
end | |
defp last_buy_queue do | |
:ets.last(@buy_queue) | |
end | |
defp first_sell_queue do | |
:ets.first(@sell_queue) | |
end | |
defp lookup_buy_order(last_buy) do | |
@buy_queue | |
|> :ets.lookup(last_buy) | |
|> List.first() | |
end | |
defp lookup_sell_order(first_sell) do | |
@sell_queue | |
|> :ets.lookup(first_sell) | |
|> List.first() | |
end | |
defp delete_last_buy_queue(last) do | |
:ets.delete(@buy_queue, last) | |
end | |
defp delete_first_sell_queue(first) do | |
:ets.delete(@sell_queue, first) | |
end | |
defp prepare_generate_tx(last_buy, first_sell) do | |
:ets.insert(@buy_queue, {last_buy, @before_generate_tx}) | |
:ets.insert(@sell_queue, {first_sell, @before_generate_tx}) | |
end | |
defp generate_tx(last_buy, first_sell) do | |
Logger.info("last_buy: #{inspect(last_buy)}, first_sell: #{inspect(first_sell)}") | |
end | |
defp generate_tx_done(last_buy, first_sell) do | |
:ets.insert(@buy_queue, {last_buy, @after_generate_tx}) | |
:ets.insert(@sell_queue, {first_sell, @after_generate_tx}) | |
end | |
defp created_new_order(last_buy, first_sell) do | |
:ets.insert(@buy_queue, {last_buy, @created_new_order}) | |
:ets.insert(@sell_queue, {first_sell, @created_new_order}) | |
end | |
# | |
end |
Author
redink
commented
Jul 31, 2018
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment