Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Mtgox Market implementation #15

Merged
merged 7 commits into from

2 participants

Steven Looman Goncalo Pinheira
Steven Looman

Mtgox Market implementation. I'll continue on ActiveParticipant when the pull request is accepted.

Feel free to comment and/or ask.

Goncalo Pinheira
Owner

Solid work. I left a comment or two there, but it's mostly ready for merging

Steven Looman

I removed the formatter by accident. Now that I'm thinking about it, the client-application of this library should specify the formatter I think. The Python logging framework provides a hierarchy in the loggers and the ability to configure the loggers in the hierarchy.
E.g., a client-application can specify which logger should print to console/write to file and how the log output should appear. I.e., using logging.getLogger(name) and user-configuration, the client-application can say: I want logging from mexbtcapi.market, but not from mexbtcapi.mtgox.

About the speed regarding value_int over value, I am currently not optimising anything but am following what I see in other files/places. We can further optimise when needed. At this stage, during development, I'm not worrying about speed.

About self._multiplier(BTC) vs self._multiplier(self.currency2), the first is easier to understand. self.currency2 should - in the case of MtGox - always be BTC as you can only trade a real currency against BTC. E.g., trading EUR to USD is not possible, but always goes 'through' BTC. self.currency1 is the variable part here, it can be EUR, or USD, or ... Its also a bit shorter and giving less hassle with regard to PEP8. If you really want me to, I can change it to self.currency2.

Thank you for your review and comments. Please let me know where you want to go.

Goncalo Pinheira
Owner

Agreed on leaving the optimization for later.

About self._multiplier(BTC), as long as mtgox doesn't suddenly allow inter-fiat-currency exchange, we're good to go. It it does, it's a couple of lines to change (for now), so we can go on with the show, I guess

About the logging: currently, there are no other loggers currently, so logging.getLogger("mexbtcapi.concepts.currency'") gets attached to the root logger.

the client-application of this library should specify the formatter I think

Ideally, the client application should be able to change the formatter (which is always true, since everything is public in python), but that doesn't mean we shouldn't provide sane defaults. Someone developing a new market shouldn't have to mess with logging to tell apart mexbtcapi's (core) messages from the market's.

Merging. I'll be stamping loggers everywhere, though, and seeing what can be done about the defaults

edit
it seems logging.basicConfig's formatter already prints the logger's name, so there's no need for a custom formatter, just for logging.basicConfig() on the hierarchy root.

Goncalo Pinheira goncalopp closed this
Goncalo Pinheira goncalopp reopened this
Goncalo Pinheira goncalopp merged commit 054d086 into from
Steven Looman

Great, thanks!

Steven Looman StevenLooman deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 15, 2013
  1. Steven Looman
  2. Steven Looman

    Fix logging

    StevenLooman authored
Commits on Jan 16, 2013
  1. Steven Looman

    Merge branch 'master' into mtgox

    StevenLooman authored
    Conflicts:
    	mexbtcapi/concepts/market.py
Commits on Jan 26, 2013
  1. Steven Looman
  2. Steven Looman
  3. Steven Looman

    PEP8'ing

    StevenLooman authored
  4. Steven Looman

    Fixing Ticker

    StevenLooman authored
This page is out of date. Refresh to see the latest.
79 mexbtcapi/api/mtgox/http_v1/high_level.py
View
@@ -1,37 +1,88 @@
-from datetime import datetime
+from datetime import datetime, timedelta
from decimal import Decimal
from functools import partial
from mexbtcapi import concepts
from mexbtcapi.concepts.currencies import BTC
-from mexbtcapi.concepts.market import Market as BaseMarket
+from mexbtcapi.concepts.currency import Amount, ExchangeRate
+from mexbtcapi.concepts.market import Market as BaseMarket, Order, Trade
import mtgox as low_level
class MtgoxTicker(concepts.market.Ticker):
- TIME_PERIOD = 24 * 60 * 60
+ TIME_PERIOD = timedelta(days=1)
class Market(BaseMarket):
MARKET_NAME = "MtGox"
def __init__(self, currency):
- BaseMarket.__init__(self, self.MARKET_NAME, BTC, currency)
+ BaseMarket.__init__(self, self.MARKET_NAME, currency, BTC)
+
# to convert low level data
- self.multiplier = low_level.multiplier[currency.name]
+ self.multiplier = low_level.multiplier
self.xchg_factory = partial(concepts.currency.ExchangeRate,
- BTC, currency)
+ currency, BTC)
+
+ def _multiplier(self, currency):
+ return self.multiplier[currency.name]
def getTicker(self):
time = datetime.now()
- data = low_level.ticker(self.c2.name)
+ data = low_level.ticker(self.currency1.name)
- data2 = [(Decimal(data[name]['value_int']) / self.multiplier)
- for name in ('high', 'low', 'avg', 'last', 'sell', 'buy')]
- hi, lo, av, la, se, bu = map(self.xchg_factory, data2)
+ data2 = [Decimal(data[name]['value_int']) /
+ self._multiplier(self.currency1)
+ for name in ('high', 'low', 'avg', 'last', 'sell', 'buy')]
+ high, low, avg, last, sell, buy = map(self.xchg_factory, data2)
- volume = long(data['vol']['value_int'])
- ticket = MtgoxTicker(market=self, time=time, high=hi, low=lo,
- average=av, last=la, sell=se, buy=bu,
+ volume = Decimal(data['vol']['value_int']) / self._multiplier(BTC)
+ ticker = MtgoxTicker(market=self, time=time, high=high, low=low,
+ average=avg, last=last, sell=sell, buy=buy,
volume=volume)
- return ticket
+ return ticker
+
+ def getDepth(self):
+ low_level_depth = low_level.depth()
+
+ return {
+ 'asks': self._depthToOrders(low_level_depth['asks'], Order.ASK),
+ 'bids': self._depthToOrders(low_level_depth['bids'], Order.BID),
+ }
+
+ def _depthToOrders(self, depth, order_type):
+ orders = []
+
+ for d in depth:
+ timestamp = datetime.fromtimestamp(d['stamp'] / 1000 / 1000)
+ amount = Amount(
+ Decimal(d['amount_int']) / self._multiplier(BTC), BTC)
+ price = ExchangeRate(
+ self.currency1, BTC,
+ Decimal(d['price_int']) / self._multiplier(self.currency1))
+ order = Order(self, timestamp, order_type, amount, price)
+ orders.append(order)
+
+ return orders
+
+ def getTrades(self):
+ low_level_trades = low_level.trades()
+
+ # convert tradres to array of Trades
+ trades = []
+ for trade in low_level_trades:
+ price = Decimal(trade['price_int']) / \
+ self._multiplier(self.currency1)
+ amount = Decimal(trade['amount_int']) / \
+ self._multiplier(BTC)
+ timestamp = datetime.fromtimestamp(trade['date'])
+
+ btc_amount = Amount(amount, BTC)
+ exchange_rate = ExchangeRate(self.currency1, BTC, price)
+
+ t = Trade(self, timestamp, btc_amount, exchange_rate)
+ t.tid = ['tid']
+
+ trades.append(t)
+
+ return trades
26 mexbtcapi/concepts/currency.py
View
@@ -1,13 +1,16 @@
from decimal import Decimal
-from mexbtcapi import log
+import logging
+
+
+logger = logging.getLogger(__name__)
def check_number_for_decimal_conversion(number):
a = type(number) in (int, long)
b = isinstance(number, (str, unicode, Decimal))
if not (a or b):
- log.warning("You are using a number (" + str(number) +
- ") that is not suitable to convert to Decimal!")
+ logger.warning("You are using a number (" + str(number) +
+ ") that is not suitable to convert to Decimal!")
class Currency(object):
@@ -18,7 +21,7 @@ def __init__(self, name):
self.name = name
def __repr__(self):
- return self.name
+ return "<Currency({0})>".format(self.name)
class ExchangeRate(object):
@@ -31,15 +34,17 @@ def __init__(self, c1, c2, exchange_rate):
# the "buy" currency
assert c1 != c2
check_number_for_decimal_conversion(exchange_rate)
- self.c1, self.c2 = c1, c2
+ self.c1 = c1
+ self.c2 = c2
self.exchange_rate = Decimal(exchange_rate)
def convert(self, amount):
assert isinstance(amount, Amount)
+
if self.c1 == amount.currency:
- return Amount(amount.value * self.exchange_rate, self.c2)
+ return Amount((1 / amount.value) * self.exchange_rate, self.c2)
elif self.c2 == amount.currency:
- return Amount((1 / amount.value) * self.exchange_rate, self.c1)
+ return Amount(amount.value * self.exchange_rate, self.c1)
else:
raise Exception("Can't exchange currencies with this ExchangeRate")
@@ -51,7 +56,8 @@ def __cmp__(self, other):
return cmp(self.exchange_rate, other.exchange_rate)
def __repr__(self):
- return "%.2f %s/%s" % (self.exchange_rate, self.c2.name, self.c1.name)
+ return "<ExchangeRate({0} {1}/{2})>".format(
+ self.exchange_rate, self.c1.name, self.c2.name)
class Amount(object):
@@ -60,7 +66,9 @@ class Amount(object):
def __init__(self, value, currency):
check_number_for_decimal_conversion(value)
- self.value, self.currency = Decimal(value), currency
+
+ self.value = Decimal(value)
+ self.currency = currency
def convert(self, currencyequivalence, to_currency):
if self.currency != to_currency:
38 mexbtcapi/concepts/market.py
View
@@ -1,5 +1,6 @@
-from datetime import datetime, timedelta
from currency import ExchangeRate, Amount
+from datetime import datetime, timedelta
+from decimal import Decimal
class Trade(object):
@@ -18,6 +19,10 @@ def __init__(self, market, timestamp, from_amount, exchange_rate):
self.from_amount = from_amount
self.exchange_rate = exchange_rate
+ @property
+ def to_amount(self):
+ return self.exchange_rate.convert(self.from_amount)
+
def __str__(self):
return "{0} -> {1}".format(self.from_amount, self.exchange_rate)
@@ -34,15 +39,15 @@ class Order(object):
parameter of the constructor.
"""
- BUY = 'BUY'
- SELL = 'SELL'
+ BID = 'BID'
+ ASK = 'ASK'
def __init__(self, market, timestamp, buy_or_sell, from_amount,
exchange_rate, properties="",
from_entity=None, to_entity=None):
assert isinstance(market, Market) # must not be null
assert isinstance(timestamp, datetime) # must not be null
- assert buy_or_sell in [self.BUY, self.SELL]
+ assert buy_or_sell in [self.BID, self.ASK]
assert isinstance(from_amount, Amount)
assert isinstance(exchange_rate, ExchangeRate)
assert isinstance(properties, str)
@@ -73,29 +78,34 @@ def __repr__(self):
class Market(object):
- """Represents a market - where Trade's are made
+ """Represents a market - where Trades are made
"""
- def __init__(self, market_name, c1, c2):
- """c1 is the "buy" currency"""
+ def __init__(self, market_name, buy_currency, sell_currency):
+ """Currency1 is the "buy" currency"""
self.name = market_name
- self.c1, self.c2 = c1, c2
+ self.currency1 = buy_currency
+ self.currency2 = sell_currency
def getTicker(self):
- """returns the most recent ticker"""
+ """Returns the most recent ticker"""
raise NotImplementedError()
- def getOpenTrades(self):
- """returns a list with all the open Trade's in the market"""
+ def getDepth(self):
+ """Returns the depth book"""
raise NotImplementedError()
- def getClosedTrades(self):
- """returns all completed trades"""
+ def getTrades(self):
+ """Returns all completed trades"""
raise NotImplementedError()
def __str__(self):
return self.name
+ def __repr__(self):
+ return "<Market({0}, {1}, {2})>".format(self.name,
+ self.currency1, self.currency2)
+
class Participant(Market):
"""Represents a participant in a market
@@ -154,7 +164,7 @@ def __init__(self, market, time, high=None, low=None, average=None,
assert isinstance(market, Market)
assert all([x is None or isinstance(x, ExchangeRate) for x in
(high, low, average, last, sell, buy)])
- assert (volume is None) or (type(volume) == long)
+ assert (volume is None) or (type(volume) == long) or (type(volume) == Decimal)
assert (buy is None and sell is None) or (buy <= sell)
assert isinstance(time, datetime)
self.market, self.time, self.volume = market, time, volume
12 mexbtcapi/logger.py
View
@@ -1,12 +0,0 @@
-import sys
-import logging
-
-
-log = logging.getLogger('mexbtcapi')
-
-hdlr = logging.StreamHandler(sys.stdout)
-formatter = logging.Formatter('MEXBTCAPI: %(message)s')
-hdlr.setFormatter(formatter)
-
-log.addHandler(hdlr)
-log.setLevel(logging.INFO)
2  mexbtcapi/util/constant_generator.py
View
@@ -3,7 +3,7 @@ def constant_generator(locals_dictionary, keys, values=None):
constants), and their values, this function assigns each constant
it's value (or an integer, if values=None) and registers them as
variables"""
- if values == None:
+ if values is None:
values = range(len(keys)) # the constants' values - integers
forward = dict(zip(keys, values)) # name to number lookup
reverse = keys # number to name lookup
Something went wrong with that request. Please try again.