Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrage #41

Closed
wants to merge 13 commits into from
419 changes: 349 additions & 70 deletions simulation/agents/arbitrageur.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions simulation/agents/centralbank.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def step(self) -> None:
self.cancel_orders()

if self.curit_target is not None:
curit_price = self.model.market_manager.curit_fiat_market.price
curit_price = self.curit_fiat_market.price
# Price is too high, it should decrease: we will sell curits at a discount.
if curit_price > hm.round_decimal(self.curit_target * (Dec(1) + self.tolerance)):
# If we have curits, sell them.
Expand Down Expand Up @@ -84,7 +84,7 @@ def step(self) -> None:
self.issue_nomins(issuance_rights)

if self.nomin_target is not None:
nomin_price = self.model.market_manager.nomin_fiat_market.price
nomin_price = self.nomin_fiat_market.price
# Price is too high, it should decrease: we will sell nomins at a discount.
if nomin_price > hm.round_decimal(self.nomin_target * (Dec(1) + self.tolerance)):
if self.available_nomins > 0:
Expand Down
115 changes: 70 additions & 45 deletions simulation/agents/marketplayer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Set, Tuple, Callable
from typing import Set, List, Tuple, Callable, Optional
from collections import namedtuple
from decimal import Decimal as Dec

Expand Down Expand Up @@ -39,10 +39,26 @@ def __init__(self, unique_id: int, havven: "model.Havven",
self.initial_wealth: Dec = self.wealth()

self.orders: Set["ob.LimitOrder"] = set()
self.trades: List["ob.TradeRecord"] = []

def __str__(self) -> str:
return self.name

@property
def curit_fiat_market(self) -> "ob.OrderBook":
"""The curit-fiat market this player trades on."""
return self.model.market_manager.curit_fiat_market

@property
def nomin_fiat_market(self) -> "ob.OrderBook":
"""The nomin-fiat market this player trades on."""
return self.model.market_manager.nomin_fiat_market

@property
def curit_nomin_market(self) -> "ob.OrderBook":
"""The curit-nomin market this player trades on."""
return self.model.market_manager.curit_nomin_market

@property
def name(self) -> str:
"""
Expand Down Expand Up @@ -201,12 +217,34 @@ def burn_nomins(self, value: Dec) -> bool:
return self.model.mint.burn_nomins(self, value)

def _sell_quoted_(self, book: "ob.OrderBook", quantity: Dec,
premium: Dec = Dec('0')) -> "ob.Bid":
premium: Dec = Dec('0')) -> Optional["ob.Bid"]:
"""
Sell a quantity of the quoted currency into the given market.
"""
price = book.lowest_ask_price()
return book.buy(hm.round_decimal(quantity/price), self, premium)

remaining_quoted = self.__getattribute__(f"available_{book.quoted}")
quantity = min(quantity, remaining_quoted)
if quantity == 0:
return None

next_qty = hm.round_decimal(min(quantity, book.lowest_ask_quantity())/book.lowest_ask_price())
pre_sold = self.__getattribute__(f"available_{book.quoted}")
bid = book.buy(next_qty, self, premium)
total_sold = pre_sold - self.__getattribute__(f"available_{book.quoted}")

while bid is not None and not bid.active and total_sold < quantity:
next_qty = hm.round_decimal(min(quantity - total_sold, book.lowest_ask_quantity())/book.lowest_ask_price())
pre_sold = self.__getattribute__(f"available_{book.quoted}")
bid = book.buy(next_qty, self, premium)
total_sold += pre_sold - self.__getattribute__(f"available_{book.quoted}")

if total_sold < quantity:
if bid is not None:
bid.cancel()
price = book.lowest_ask_price()
bid = book.bid(price, hm.round_decimal((quantity - total_sold)/price), self)

return bid

def _sell_base_(self, book: "ob.OrderBook", quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
Expand All @@ -220,48 +258,42 @@ def sell_nomins_for_curits(self, quantity: Dec,
"""
Sell a quantity of nomins to buy curits.
"""
return self._sell_quoted_(self.model.market_manager.curit_nomin_market,
quantity, premium)
return self._sell_quoted_(self.curit_nomin_market, quantity, premium)

def sell_curits_for_nomins(self, quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
"""
Sell a quantity of curits to buy nomins.
"""
return self._sell_base_(self.model.market_manager.curit_nomin_market,
quantity, discount)
return self._sell_base_(self.curit_nomin_market, quantity, discount)

def sell_fiat_for_curits(self, quantity: Dec,
premium: Dec = Dec('0')) -> "ob.Bid":
"""
Sell a quantity of fiat to buy curits.
"""
return self._sell_quoted_(self.model.market_manager.curit_fiat_market,
quantity, premium)
return self._sell_quoted_(self.curit_fiat_market, quantity, premium)

def sell_curits_for_fiat(self, quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
"""
Sell a quantity of curits to buy fiat.
"""
return self._sell_base_(self.model.market_manager.curit_fiat_market,
quantity, discount)
return self._sell_base_(self.curit_fiat_market, quantity, discount)

def sell_fiat_for_nomins(self, quantity: Dec,
premium: Dec = Dec('0')) -> "ob.Bid":
"""
Sell a quantity of fiat to buy nomins.
"""
return self._sell_quoted_(self.model.market_manager.nomin_fiat_market,
quantity, premium)
return self._sell_quoted_(self.nomin_fiat_market, quantity, premium)

def sell_nomins_for_fiat(self, quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
"""
Sell a quantity of nomins to buy fiat.
"""
return self._sell_base_(self.model.market_manager.nomin_fiat_market,
quantity, discount)
return self._sell_base_(self.nomin_fiat_market, quantity, discount)

def _sell_quoted_with_fee_(self, received_qty_fn: Callable[[Dec], Dec],
book: "ob.OrderBook", quantity: Dec,
Expand All @@ -270,8 +302,7 @@ def _sell_quoted_with_fee_(self, received_qty_fn: Callable[[Dec], Dec],
Sell a quantity of the quoted currency into the given market, including the
fee, as calculated by the provided function.
"""
price = book.lowest_ask_price()
return book.buy(received_qty_fn(hm.round_decimal(quantity/price)), self, premium)
return self._sell_quoted_(book, received_qty_fn(quantity), premium)

def _sell_base_with_fee_(self, received_qty_fn: Callable[[Dec], Dec],
book: "ob.OrderBook", quantity: Dec,
Expand All @@ -280,97 +311,91 @@ def _sell_base_with_fee_(self, received_qty_fn: Callable[[Dec], Dec],
Sell a quantity of the base currency into the given market, including the
fee, as calculated by the provided function.
"""
return book.sell(received_qty_fn(quantity), self, discount)
return self._sell_base_(book, received_qty_fn(quantity), discount)

def sell_nomins_for_curits_with_fee(self, quantity: Dec,
premium: Dec = Dec('0')) -> "ob.Bid":
"""
Sell a quantity of nomins (including fee) to buy curits.
"""
return self._sell_quoted_with_fee_(self.model.fee_manager.transferred_nomins_received,
self.model.market_manager.curit_nomin_market,
quantity, premium)
self.curit_nomin_market, quantity, premium)

def sell_curits_for_nomins_with_fee(self, quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
"""
Sell a quantity of curits (including fee) to buy nomins.
"""
return self._sell_base_with_fee_(self.model.fee_manager.transferred_curits_received,
self.model.market_manager.curit_nomin_market,
quantity, discount)
self.curit_nomin_market, quantity, discount)

def sell_fiat_for_curits_with_fee(self, quantity: Dec,
premium: Dec = Dec('0')) -> "ob.Bid":
"""
Sell a quantity of fiat (including fee) to buy curits.
"""
return self._sell_quoted_with_fee_(self.model.fee_manager.transferred_fiat_received,
self.model.market_manager.curit_fiat_market,
quantity, premium)
self.curit_fiat_market, quantity, premium)

def sell_curits_for_fiat_with_fee(self, quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
"""
Sell a quantity of curits (including fee) to buy fiat.
"""
return self._sell_base_with_fee_(self.model.fee_manager.transferred_curits_received,
self.model.market_manager.curit_fiat_market,
quantity, discount)
self.curit_fiat_market, quantity, discount)

def sell_fiat_for_nomins_with_fee(self, quantity: Dec,
premium: Dec = Dec('0')) -> "ob.Bid":
"""
Sell a quantity of fiat (including fee) to buy nomins.
"""
return self._sell_quoted_with_fee_(self.model.fee_manager.transferred_fiat_received,
self.model.market_manager.nomin_fiat_market,
quantity, premium)
self.nomin_fiat_market, quantity, premium)

def sell_nomins_for_fiat_with_fee(self, quantity: Dec,
discount: Dec = Dec('0')) -> "ob.Ask":
"""
Sell a quantity of nomins (including fee) to buy fiat.
"""
return self._sell_base_with_fee_(self.model.fee_manager.transferred_nomins_received,
self.model.market_manager.nomin_fiat_market,
quantity, discount)
self.nomin_fiat_market, quantity, discount)

def place_curit_fiat_bid(self, quantity: Dec, price: Dec) -> "ob.Bid":
"""
Place a bid for a quantity of curits, at a price in fiat.
"""
return self.model.market_manager.curit_fiat_market.bid(price, quantity, self)
return self.curit_fiat_market.bid(price, quantity, self)

def place_curit_fiat_ask(self, quantity: Dec, price: Dec) -> "ob.Ask":
"""
Place an ask for fiat with a quantity of curits, at a price in fiat.
"""
return self.model.market_manager.curit_fiat_market.ask(price, quantity, self)
return self.curit_fiat_market.ask(price, quantity, self)

def place_nomin_fiat_bid(self, quantity: Dec, price: Dec) -> "ob.Bid":
"""
Place a bid for a quantity of nomins, at a price in fiat.
"""
return self.model.market_manager.nomin_fiat_market.bid(price, quantity, self)
return self.nomin_fiat_market.bid(price, quantity, self)

def place_nomin_fiat_ask(self, quantity: Dec, price: Dec) -> "ob.Ask":
"""
Place an ask for fiat with a quantity of nomins, at a price in fiat.
"""
return self.model.market_manager.nomin_fiat_market.ask(price, quantity, self)
return self.nomin_fiat_market.ask(price, quantity, self)

def place_curit_nomin_bid(self, quantity: Dec, price: Dec) -> "ob.Bid":
"""
Place a bid for a quantity of curits, at a price in nomins.
"""
return self.model.market_manager.curit_nomin_market.bid(price, quantity, self)
return self.curit_nomin_market.bid(price, quantity, self)

def place_curit_nomin_ask(self, quantity: Dec, price: Dec) -> "ob.Ask":
"""
Place an ask for nomins with a quantity of curits, at a price in nomins.
"""
return self.model.market_manager.curit_nomin_market.ask(price, quantity, self)
return self.curit_nomin_market.ask(price, quantity, self)

def place_curit_fiat_bid_with_fee(self, quantity: Dec, price: Dec) -> "ob.Bid":
"""
Expand All @@ -379,14 +404,14 @@ def place_curit_fiat_bid_with_fee(self, quantity: Dec, price: Dec) -> "ob.Bid":
# Note, only works because the fee is multiplicative, we're calculating the fee not
# on the quantity we are actually transferring, which is (quantity*price)
qty = self.model.fee_manager.transferred_fiat_received(quantity)
return self.model.market_manager.curit_fiat_market.bid(price, qty, self)
return self.curit_fiat_market.bid(price, qty, self)

def place_curit_fiat_ask_with_fee(self, quantity: Dec, price: Dec) -> "ob.Ask":
"""
Place an ask for fiat with a quantity of curits, including the fee, at a price in fiat.
"""
qty = self.model.fee_manager.transferred_curits_received(quantity)
return self.model.market_manager.curit_fiat_market.ask(price, qty, self)
return self.curit_fiat_market.ask(price, qty, self)

def place_nomin_fiat_bid_with_fee(self, quantity: Dec, price: Dec) -> "ob.Bid":
"""
Expand All @@ -395,14 +420,14 @@ def place_nomin_fiat_bid_with_fee(self, quantity: Dec, price: Dec) -> "ob.Bid":
# Note, only works because the fee is multiplicative, we're calculating the fee not
# on the quantity we are actually transferring, which is (quantity*price)
qty = self.model.fee_manager.transferred_fiat_received(quantity)
return self.model.market_manager.nomin_fiat_market.bid(price, qty, self)
return self.nomin_fiat_market.bid(price, qty, self)

def place_nomin_fiat_ask_with_fee(self, quantity: Dec, price: Dec) -> "ob.Ask":
"""
Place an ask for fiat with a quantity of nomins, including the fee, at a price in fiat.
"""
qty = self.model.fee_manager.transferred_nomins_received(quantity)
return self.model.market_manager.nomin_fiat_market.ask(price, qty, self)
return self.nomin_fiat_market.ask(price, qty, self)

def place_curit_nomin_bid_with_fee(self, quantity: Dec, price: Dec) -> "ob.Bid":
"""
Expand All @@ -411,14 +436,14 @@ def place_curit_nomin_bid_with_fee(self, quantity: Dec, price: Dec) -> "ob.Bid":
# Note, only works because the fee is multiplicative, we're calculating the fee not
# on the quantity we are actually transferring, which is (quantity*price)
qty = self.model.fee_manager.transferred_nomins_received(quantity)
return self.model.market_manager.curit_nomin_market.bid(price, qty, self)
return self.curit_nomin_market.bid(price, qty, self)

def place_curit_nomin_ask_with_fee(self, quantity: Dec, price: Dec) -> "ob.Ask":
"""
Place an ask for nomins with a quantity of curits, including the fee, at a price in nomins.
"""
qty = self.model.fee_manager.transferred_curits_received(quantity)
return self.model.market_manager.curit_nomin_market.ask(price, qty, self)
return self.curit_nomin_market.ask(price, qty, self)

@property
def available_fiat(self) -> Dec:
Expand Down Expand Up @@ -447,11 +472,11 @@ def notify_cancelled(self, order: "ob.LimitOrder") -> None:
"""
pass

def notify_filled(self, order: "ob.LimitOrder") -> None:
def notify_trade(self, record: "ob.TradeRecord") -> None:
"""
Notify this agent that its order was filled.
"""
pass
self.trades.append(record)

def step(self) -> None:
pass
4 changes: 2 additions & 2 deletions simulation/agents/nomin_shorter.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def step(self) -> None:

def _find_best_nom_fiat_trade(self) -> Optional[Tuple[Dec, Dec]]:
trade_price_quant = None
for bid in self.model.market_manager.nomin_fiat_market.highest_bids():
for bid in self.nomin_fiat_market.highest_bids():
if bid.price < self._nomin_sell_rate_threshold:
break
if trade_price_quant is not None:
Expand All @@ -74,7 +74,7 @@ def _make_nom_fiat_trade(self, trade_price_quant: Tuple[Dec, Dec]) -> "ob.Ask":

def _find_best_fiat_nom_trade(self) -> Optional[Tuple[Dec, Dec]]:
trade_price_quant = None
for ask in self.model.market_manager.nomin_fiat_market.lowest_asks():
for ask in self.nomin_fiat_market.lowest_asks():
if ask.price > self._nomin_buy_rate_threshold:
break
if trade_price_quant is not None:
Expand Down
12 changes: 6 additions & 6 deletions simulation/agents/randomizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,31 +45,31 @@ def step(self) -> None:
break

def _curit_fiat_bid_(self) -> "ob.Bid":
price = self.model.market_manager.curit_fiat_market.price
price = self.curit_fiat_market.price
movement = hm.round_decimal(Dec(2*random.random() - 1) * price * self.variance)
return self.place_curit_fiat_bid(self._fraction_(self.available_fiat, Dec(10)), price + movement)

def _curit_fiat_ask_(self) -> "ob.Ask":
price = self.model.market_manager.curit_fiat_market.price
price = self.curit_fiat_market.price
movement = hm.round_decimal(Dec(2*random.random() - 1) * price * self.variance)
return self.place_curit_fiat_ask(self._fraction_(self.available_curits, Dec(10)), price + movement)

def _nomin_fiat_bid_(self) -> "ob.Bid":
price = self.model.market_manager.nomin_fiat_market.price
price = self.nomin_fiat_market.price
movement = hm.round_decimal(Dec(2*random.random() - 1) * price * self.variance)
return self.place_nomin_fiat_bid(self._fraction_(self.available_fiat, Dec(10)), price + movement)

def _nomin_fiat_ask_(self) -> "ob.Ask":
price = self.model.market_manager.nomin_fiat_market.price
price = self.nomin_fiat_market.price
movement = hm.round_decimal(Dec(2*random.random() - 1) * price * self.variance)
return self.place_nomin_fiat_ask(self._fraction_(self.available_nomins, Dec(10)), price + movement)

def _curit_nomin_bid_(self) -> "ob.Bid":
price = self.model.market_manager.curit_nomin_market.price
price = self.curit_nomin_market.price
movement = hm.round_decimal(Dec(2*random.random() - 1) * price * self.variance)
return self.place_curit_nomin_bid(self._fraction_(self.available_nomins, Dec(10)), price + movement)

def _curit_nomin_ask_(self) -> "ob.Ask":
price = self.model.market_manager.curit_nomin_market.price
price = self.curit_nomin_market.price
movement = hm.round_decimal(Dec(2*random.random() - 1) * price * self.variance)
return self.place_curit_nomin_ask(self._fraction_(self.available_curits, Dec(10)), price + movement)
Loading