def __init__(self): super().__init__() self.liquidation_ratio = self.tub.mat() self.minimum_ratio = self.liquidation_ratio + Ray.from_number( self.arguments.min_margin) self.target_ratio = self.liquidation_ratio + Ray.from_number( self.arguments.top_up_margin)
def test_should_identify_multi_step_opportunities(self, token1, token2, token3, token4): # given conversion1 = Conversion(token1, token2, Ray.from_number(1.02), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token3, Ray.from_number(1.03), Wad.from_number(10000), 'met2') conversion3 = Conversion(token3, token4, Ray.from_number(1.05), Wad.from_number(10000), 'met3') conversion4 = Conversion(token4, token1, Ray.from_number(1.07), Wad.from_number(10000), 'met4') conversions = [conversion1, conversion2, conversion3, conversion4] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 1 assert len(opportunities[0].steps) == 4 assert opportunities[0].steps[0].method == "met1" assert opportunities[0].steps[1].method == "met2" assert opportunities[0].steps[2].method == "met3" assert opportunities[0].steps[3].method == "met4"
def test_should_ignore_irrelevant_conversions(self, token1, token2, token3, token4): # given conversion1 = Conversion(token1, token2, Ray.from_number(1.02), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token1, Ray.from_number(1.03), Wad.from_number(10000), 'met2') conversion3 = Conversion(token1, token3, Ray.from_number(1.04), Wad.from_number(10000), 'met3') conversion4 = Conversion(token1, token4, Ray.from_number(1.07), Wad.from_number(10000), 'met4') conversion5 = Conversion(token2, token4, Ray.from_number(1.08), Wad.from_number(10000), 'met5') conversions = [ conversion1, conversion2, conversion3, conversion4, conversion5 ] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 1 assert len(opportunities[0].steps) == 2 assert opportunities[0].steps[0].method == "met1" assert opportunities[0].steps[1].method == "met2"
def test_should_adjust_amounts_based_on_max_source_amount( self, token1, token2, token3, token4): # given conversion1 = Conversion(token1, token2, Ray.from_number(2.0), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token3, Ray.from_number(1.6), Wad.from_number(10000), 'met2') conversion3 = Conversion(token3, token4, Ray.from_number(1.2), Wad.from_number(100), 'met3') conversion4 = Conversion(token4, token1, Ray.from_number(1.1), Wad.from_number(10000), 'met4') conversions = [conversion1, conversion4, conversion3, conversion2] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 1 assert len(opportunities[0].steps) == 4 assert opportunities[0].steps[0].method == "met1" assert opportunities[0].steps[0].source_amount == Wad.from_number( 31.25) assert opportunities[0].steps[0].target_amount == Wad.from_number(62.5) assert opportunities[0].steps[1].method == "met2" assert opportunities[0].steps[1].source_amount == Wad.from_number(62.5) assert opportunities[0].steps[1].target_amount == Wad.from_number(100) assert opportunities[0].steps[2].method == "met3" assert opportunities[0].steps[2].source_amount == Wad.from_number(100) assert opportunities[0].steps[2].target_amount == Wad.from_number(120) assert opportunities[0].steps[3].method == "met4" assert opportunities[0].steps[3].source_amount == Wad.from_number(120) assert opportunities[0].steps[3].target_amount == Wad.from_number(132)
def __init__(self, tub: Tub, tap: Tap): self.tub = tub self.tap = tap super().__init__(source_token=self.tub.sai(), target_token=self.tub.skr(), rate=(Ray.from_number(1) / Ray(tap.ask())), max_source_amount=self.bustable_amount_in_sai(tap), method="tub.bust()")
def __init__(self, otc: SimpleMarket, offer: OfferInfo): self.otc = otc self.offer = offer super().__init__(source_token=offer.buy_which_token, target_token=offer.sell_which_token, rate=Ray(offer.sell_how_much)/Ray(offer.buy_how_much), max_source_amount=offer.buy_how_much, method=f"opc.take({self.offer.offer_id})")
def test_coax_and_way(self, sai: SaiDeployment): # given assert sai.tub.way() == Ray.from_number(1) # when sai.tub.coax(Ray.from_number(1.00000000000000007)) # then assert sai.tub.way() == Ray.from_number(1.00000000000000007)
def test_cuff_and_mat(self, sai: SaiDeployment): # given assert sai.tub.mat() == Ray.from_number(1) # when sai.tub.cuff(Ray.from_number(1.5)) # then assert sai.tub.mat() == Ray.from_number(1.5)
def test_crop_and_tax(self, sai: SaiDeployment): # given assert sai.tub.tax() == Ray.from_number(1) # when sai.tub.crop(Ray.from_number(1.00000000000000002)) # then assert sai.tub.tax() == Ray.from_number(1.00000000000000002)
def test_chop_and_axe(self, sai: SaiDeployment): # given assert sai.tub.axe() == Ray.from_number(1) sai.tub.cuff(Ray.from_number(1.5)) # when sai.tub.chop(Ray.from_number(1.2)) # then assert sai.tub.axe() == Ray.from_number(1.2)
def test_safe(self, sai: SaiDeployment): # given sai.tub.cuff(Ray.from_number(1.5)) sai.tub.chop(Ray.from_number(1.2)) # when sai.tub.open() # then assert sai.tub.safe(1)
def test_should_calculate_total_rate(self, token1, token2): # given step1 = Conversion(token1, token2, Ray.from_number(1.01), Wad.from_number(1000), 'met1') step2 = Conversion(token2, token1, Ray.from_number(1.02), Wad.from_number(1000), 'met2') # when sequence = Sequence([step1, step2]) # then assert sequence.total_rate() == Ray.from_number(1.0302)
def per(self) -> Ray: """Get the lps per ref ratio. Returns: The current lps per ref ratio. """ return Ray(self._contract.call().per())
def test_should_recognize_if_there_are_no_opportunities( self, token1, token2, token3): # given conversion1 = Conversion(token1, token2, Ray.from_number(1.02), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token3, Ray.from_number(1.03), Wad.from_number(10000), 'met2') conversions = [conversion1, conversion2] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 0
def jar_bid(self) -> Ray: """Get the current `exit()` price (GEM per SKR). Returns: The GEM per SKR price that will be used on `exit()`. """ return Ray(self._contractJar.call().bid())
def mat(self) -> Ray: """Get the liquidation ratio. Returns: The liquidation ratio. `1.5` means the liquidation ratio is 150%. """ return Ray(self._contractTub.call().mat())
def __init__(self, tub: Tub): self.tub = tub super().__init__(source_token=self.tub.gem(), target_token=self.tub.skr(), rate=(Ray.from_number(1) / tub.jar_ask()), max_source_amount=Wad.from_number(1000000), #1 mio ETH = infinity ;) method="tub.join()")
def tax(self) -> Ray: """Get the stability fee. Returns: Per-second value of the stability fee. `1.0` means no stability fee. """ return Ray(self._contractTub.call().tax())
def way(self) -> Ray: """Get the holder fee (interest rate). Returns: Per-second value of the holder fee. `1.0` means no holder fee. """ return Ray(self._contractTip.call().way())
def total_rate(self) -> Ray: """Calculates the multiplication of all conversion rates forming this sequence. A `total_rate` > 1.0 is a general indication that executing this sequence may be profitable. """ return reduce(operator.mul, map(lambda step: step.rate, self.steps), Ray.from_number(1.0))
def fix(self) -> Ray: """Get the GEM per SAI settlement price. Returns: The GEM per SAI settlement (kill) price. """ return Ray(self._contract.call().fix())
def jar_ask(self) -> Ray: """Get the current `join()` price (GEM per SKR). Returns: The GEM per SKR price that will be used on `join()`. """ return Ray(self._contractJar.call().ask())
def axe(self) -> Ray: """Get the liquidation penalty. Returns: The liquidation penalty. `1.0` means no penalty. `1.2` means 20% penalty. """ return Ray(self._contractTub.call().axe())
def fit(self) -> Ray: """Get the GEM per SKR settlement price. Returns: The GEM per SKR settlement (kill) price. """ return Ray(self._contractTub.call().fit())
def __init__(self, tub: Tub, tap: Tap): self.tub = tub self.tap = tap super().__init__(source_token=self.tub.skr(), target_token=self.tub.sai(), rate=Ray(tap.bid()), max_source_amount=self.boomable_amount_in_skr(tap), method="tub.boom()")
def test_nicely_convert_to_string_without_amounts(token1, token2): # given conversion = Conversion(token1, token2, Ray.from_number(1.01), Wad.from_number(1000), 'met()') # expect assert str( conversion ) == "[TK1 -> TK2 @1.010000000000000000000000000 by met() (max=1000.000000000000000000 TK1)]"
def test_should_identify_opportunity(self, token1, token2): # given conversion1 = Conversion(token1, token2, Ray.from_number(1.02), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token1, Ray.from_number(1.03), Wad.from_number(10000), 'met2') conversions = [conversion1, conversion2] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 1 assert len(opportunities[0].steps) == 2 assert opportunities[0].steps[0].method == "met1" assert opportunities[0].steps[1].method == "met2"
def __init__(self, lpc: Lpc): self.lpc = lpc rate = Ray(self.lpc.par() / (self.lpc.tag() * self.lpc.gap())) #TODO we always leave 0.000001 in the liquidity pool, in case of some rounding errors max_entry_ref = Wad.max((ERC20Token(web3=lpc.web3, address=lpc.alt()).balance_of(lpc.address) / Wad(rate)) - Wad.from_number(0.000001), Wad.from_number(0)) super().__init__(source_token=self.lpc.ref(), target_token=self.lpc.alt(), rate=rate, max_source_amount=max_entry_ref, method="lpc.take(alt)")
def test_should_identify_all_opportunities_regardless_whether_they_are_profitable( self, token1, token2): # given conversion1 = Conversion(token1, token2, Ray.from_number(1.1), Wad.from_number(10000), 'met1') conversion2 = Conversion(token2, token1, Ray.from_number(0.6), Wad.from_number(10000), 'met2') conversions = [conversion1, conversion2] base_token = token1 # when opportunities = OpportunityFinder(conversions).find_opportunities( base_token, Wad.from_number(100)) # then assert len(opportunities) == 1 assert len(opportunities[0].steps) == 2 assert opportunities[0].steps[0].method == "met1" assert opportunities[0].steps[1].method == "met2"
def test_should_calculate_tx_costs(self, token1): # expect the tx_costs to be non negative and to increase with the number of steps steps = [] prev_tx_costs = Wad.from_number(0) for i in range(10): steps.append(Conversion(token1, token1, Ray(0), Wad(0), 'met')) opportunity = Sequence(steps) tx_costs = opportunity.tx_costs() assert (tx_costs > prev_tx_costs) prev_tx_costs = tx_costs