Skip to content

Commit 1992241

Browse files
authored
Merge pull request #203 from s4w3d0ff/dev
v0.5.7
2 parents 44bdc98 + c8294bb commit 1992241

File tree

5 files changed

+108
-131
lines changed

5 files changed

+108
-131
lines changed

README.md

Lines changed: 27 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![python](https://img.shields.io/badge/python-2.7%20%26%203-blue.svg)![licence](https://img.shields.io/badge/licence-GPL%20v2-blue.svg)](https://github.com/s4w3d0ff/python-poloniex/blob/master/LICENSE) [![release](https://img.shields.io/github/release/s4w3d0ff/python-poloniex.svg)![release build](https://travis-ci.org/s4w3d0ff/python-poloniex.svg?branch=v0.5.6)](https://github.com/s4w3d0ff/python-poloniex/releases)
1+
[![python](https://img.shields.io/badge/python-2.7%20%26%203-blue.svg)![licence](https://img.shields.io/badge/licence-GPL%20v2-blue.svg)](https://github.com/s4w3d0ff/python-poloniex/blob/master/LICENSE) [![release](https://img.shields.io/github/release/s4w3d0ff/python-poloniex.svg)![release build](https://travis-ci.org/s4w3d0ff/python-poloniex.svg?branch=v0.5.7)](https://github.com/s4w3d0ff/python-poloniex/releases)
22
[![master](https://img.shields.io/badge/branch-master-blue.svg)![master build](https://api.travis-ci.org/s4w3d0ff/python-poloniex.svg?branch=master)](https://github.com/s4w3d0ff/python-poloniex/tree/master) [![dev](https://img.shields.io/badge/branch-dev-blue.svg)![dev build](https://api.travis-ci.org/s4w3d0ff/python-poloniex.svg?branch=dev)](https://github.com/s4w3d0ff/python-poloniex/tree/dev)
33

44
Inspired by [this](http://pastebin.com/8fBVpjaj) wrapper written by 'oipminer'
@@ -77,60 +77,31 @@ print(polo.returnTradeHistory('BTC_ETH'))
7777
You can also not use the 'helper' methods at all and use `poloniex.PoloniexBase` which only has `returnMarketHist` and `__call__` to make rest api calls.
7878

7979
#### Websocket Usage:
80-
To connect to the websocket api just create a child class of `PoloniexSocketed` like so:
80+
To connect to the websocket api use the `PoloniexSocketed` class like so:
8181
```python
8282
import poloniex
8383
import logging
84+
from time import sleep
8485

85-
logging.basicConfig()
86-
87-
class MySocket(poloniex.PoloniexSocketed):
88-
89-
def on_heartbeat(self, msg):
90-
"""
91-
Triggers whenever we get a heartbeat message
92-
"""
93-
print(msg)
94-
95-
def on_volume(self, msg):
96-
"""
97-
Triggers whenever we get a 24hvolume message
98-
"""
99-
print(msg)
100-
101-
def on_ticker(self, msg):
102-
"""
103-
Triggers whenever we get a ticker message
104-
"""
105-
print(msg)
106-
107-
def on_market(self, msg):
108-
"""
109-
Triggers whenever we get a market ('currencyPair') message
110-
"""
111-
print(msg)
112-
113-
def on_account(self, msg):
114-
"""
115-
Triggers whenever we get an account message
116-
"""
117-
print(msg)
118-
119-
sock = MySocket()
12086
# helps show what is going on
121-
sock.logger.setLevel(logging.DEBUG)
122-
# start the websocket thread and subscribe to '24hvolume'
123-
sock.startws(subscribe=['24hvolume'])
87+
logging.basicConfig()
88+
poloniex.logger.setLevel(logging.DEBUG)
89+
90+
def on_volume(data):
91+
print(data)
92+
# make instance
93+
sock = poloniex.PoloniexSocketed()
94+
# start the websocket thread and subscribe to '24hvolume' setting the callback to 'on_volume'
95+
sock.startws(subscribe={'24hvolume': on_volume})
12496
# give the socket some time to init
125-
poloniex.sleep(5)
126-
# this won't work:
127-
#sock.subscribe('ticker')
128-
# use channel id to un/sub
129-
sock.subscribe('1002')
130-
poloniex.sleep(1)
131-
# unsub from ticker
132-
sock.unsubscribe('1002')
133-
poloniex.sleep(4)
97+
sleep(5)
98+
# use the channel name str or id int to subscribe/unsubscribe
99+
sock.subscribe(chan='ticker', callback=print)
100+
sleep(1)
101+
# unsub from ticker using id (str name can be use as well)
102+
sock.unsubscribe(1002)
103+
sleep(4)
104+
# stop websocket
134105
sock.stopws()
135106

136107
```
@@ -152,5 +123,12 @@ DEBUG:poloniex:Unsubscribed to ticker
152123
DEBUG:poloniex:Websocket Closed
153124
INFO:poloniex:Websocket thread stopped/joined
154125
```
126+
You can also subscribe and start the websocket thread when creating an instance of `PoloniexSocketed` by using the `subscribe` and `start` args:
127+
```python
128+
129+
sock = poloniex.PoloniexSocketed(subscribe={'24hvolume': print}, start=True)
130+
131+
```
132+
155133

156134
**More examples of how to use websocket push API can be found [here](https://github.com/s4w3d0ff/python-poloniex/tree/master/examples).**

examples/websocket/dictTicker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def on_ticker(self, data):
4545
polo = TickPolo()
4646
poloniex.logging.basicConfig()
4747
polo.logger.setLevel(poloniex.logging.DEBUG)
48-
polo.startws(['ticker'])
48+
polo.startws({'ticker': polo.on_ticker})
4949
for i in range(3):
5050
pprint(polo.ticker('BTC_LTC'))
5151
poloniex.sleep(10)

examples/websocket/stopLimit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def __init__(self, *args, **kwargs):
99
def on_ticker(self, msg):
1010
data = [float(dat) for dat in msg]
1111
# check stop orders
12-
mkt = self.channels[str(int(data[0]))]['name']
12+
mkt = self._getChannelName(str(int(data[0])))
1313
la = data[2]
1414
hb = data[3]
1515
for id in self.stopOrders:
@@ -98,6 +98,6 @@ def callbk(id):
9898
callback=callbk,
9999
# remove or set 'test' to false to place real orders
100100
test=True)
101-
test.startws(['ticker'])
101+
test.startws({'ticker': test.on_ticker})
102102
poloniex.sleep(120)
103103
test.stopws(3)

poloniex/__init__.py

Lines changed: 77 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def moveOrder(self, orderNumber, rate, amount=False, orderType=False):
530530

531531
return self.__call__('moveOrder', args)
532532

533-
def withdraw(self, currency, amount, address, paymentId=False):
533+
def withdraw(self, currency, amount, address, paymentId=False, currencyToWithdrawAs=False ):
534534
""" Immediately places a withdrawal for a given currency, with no email
535535
confirmation. In order to use this method, the withdrawal privilege
536536
must be enabled for your API key. Required parameters are
@@ -543,6 +543,8 @@ def withdraw(self, currency, amount, address, paymentId=False):
543543
}
544544
if paymentId:
545545
args['paymentId'] = str(paymentId)
546+
if currencyToWithdrawAs:
547+
args['currencyToWithdrawAs'] = str(currencyToWithdrawAs)
546548
return self.__call__('withdraw', args)
547549

548550
def returnFeeInfo(self):
@@ -681,8 +683,8 @@ def toggleAutoRenew(self, orderNumber):
681683

682684
class PoloniexSocketed(Poloniex):
683685
""" Child class of Poloniex with support for the websocket api """
684-
def __init__(self, *args, **kwargs):
685-
super(PoloniexSocketed, self).__init__(*args, **kwargs)
686+
def __init__(self, key=None, secret=None, subscribe={}, start=False, *args, **kwargs):
687+
super(PoloniexSocketed, self).__init__(key, secret, *args, **kwargs)
686688
self.socket = WebSocketApp(url="wss://api2.poloniex.com/",
687689
on_open=self.on_open,
688690
on_message=self.on_message,
@@ -691,54 +693,30 @@ def __init__(self, *args, **kwargs):
691693
self._t = None
692694
self._running = False
693695
self.channels = {
694-
'1000': {'name': 'account',
695-
'sub': False,
696-
'callback': self.on_account},
697-
'1002': {'name': 'ticker',
698-
'sub': False,
699-
'callback': self.on_ticker},
700-
'1003': {'name': '24hvolume',
701-
'sub': False,
702-
'callback': self.on_volume},
703-
'1010': {'name': 'heartbeat',
704-
'sub': False,
705-
'callback': self.on_heartbeat},
696+
'account': {'id': '1000'},
697+
'ticker': {'id': '1002'},
698+
'24hvolume': {'id': '1003'},
699+
'heartbeat': {'id': '1010',
700+
'callback': self.on_heartbeat},
706701
}
707702
# add each market to channels list by id
708-
# (wish there was a cleaner way of doing this...)
709703
tick = self.returnTicker()
710704
for market in tick:
711-
self.channels[str(tick[market]['id'])] = {
712-
'name': market,
713-
'sub': False,
714-
'callback': self.on_market
715-
}
705+
self.channels[market] = {'id': str(tick[market]['id'])}
706+
# handle init subscribes
707+
if subscribe:
708+
self.setSubscribes(**subscribe)
709+
if start:
710+
self.startws()
716711

717-
def _handle_sub(self, message):
718-
""" Handles websocket un/subscribe messages """
719-
chan = str(message[0])
720-
# skip heartbeats
721-
if not chan == '1010':
722-
# Subscribed
723-
if message[1] == 1:
724-
# update self.channels[chan]['sub'] flag
725-
self.channels[chan]['sub'] = True
726-
self.logger.debug('Subscribed to %s', self.channels[chan]['name'])
727-
# return False so no callback trigger
728-
return False
729-
# Unsubscribed
730-
if message[1] == 0:
731-
# update self.channels[chan]['sub'] flag
732-
self.channels[chan]['sub'] = False
733-
self.logger.debug('Unsubscribed to %s', self.channels[chan]['name'])
734-
# return False so no callback trigger
735-
return False
736-
# return chan name
737-
return chan
712+
def _getChannelName(self, id):
713+
return next(
714+
(chan for chan in self.channels if self.channels[chan]['id'] == id),
715+
False)
738716

739717
def on_open(self, *ws):
740718
for chan in self.channels:
741-
if self.channels[chan]['sub']:
719+
if 'sub' in self.channels[chan] and self.channels[chan]['sub']:
742720
self.subscribe(chan)
743721

744722
def on_message(self, data):
@@ -751,45 +729,63 @@ def on_message(self, data):
751729
# catch errors
752730
if 'error' in message:
753731
return self.logger.error(message['error'])
732+
chan = self._getChannelName(str(message[0]))
754733
# handle sub/unsub
755-
chan = self._handle_sub(message)
756-
if chan:
734+
# skip heartbeats
735+
if not chan == 'heartbeat':
736+
# Subscribed
737+
if message[1] == 1:
738+
self.logger.debug('Subscribed to %s', chan)
739+
# return False so no callback trigger
740+
return False
741+
# Unsubscribed
742+
if message[1] == 0:
743+
self.logger.debug('Unsubscribed to %s', chan)
744+
# return False so no callback trigger
745+
return False
746+
if 'callback' in self.channels[chan]:
757747
# activate chan callback
758-
# heartbeats are funky
759-
if not chan == '1010':
748+
if not chan in ['account', 'heartbeat']:
760749
message = message[2]
761750
self.socket._callback(self.channels[chan]['callback'], message)
762751

763752
def on_error(self, error):
764753
self.logger.error(error)
765754

766755
def on_close(self, *args):
767-
self.logger.debug('Websocket Closed')
768-
769-
def on_ticker(self, args):
770-
self.logger.debug(args)
771-
772-
def on_account(self, args):
773-
self.logger.debug(args)
756+
self.logger.info('Websocket Closed')
774757

775-
def on_market(self, args):
758+
def on_heartbeat(self, args):
776759
self.logger.debug(args)
777760

778-
def on_volume(self, args):
779-
self.logger.debug(args)
761+
def setCallback(self, chan, callback):
762+
""" Sets the callback function for <chan> """
763+
if isinstance(chan, int):
764+
chan = self._getChannelName(str(chan))
765+
self.channels[chan]['callback'] = callback
780766

781-
def on_heartbeat(self, args):
782-
self.logger.debug(args)
767+
def setSubscribes(self, **subs):
768+
for sub in subs:
769+
if not sub in self.channels:
770+
self.logger.warning('Invalid channel: %s', sub)
771+
else:
772+
self.channels[sub]['sub'] = True
773+
self.channels[sub]['callback'] = subs[sub]
783774

784-
def subscribe(self, chan):
775+
def subscribe(self, chan, callback=None):
785776
""" Sends the 'subscribe' command for <chan> """
777+
if isinstance(chan, int):
778+
chan = self._getChannelName(str(chan))
786779
# account chan?
787-
if chan in ['1000', 1000]:
780+
if chan == 'account':
788781
# sending commands to 'account' requires a key, secret and nonce
789782
if not self.key or not self.secret:
790783
raise PoloniexError(
791784
"self.key and self.secret needed for 'account' channel"
792785
)
786+
self.channels[chan]['sub'] = True
787+
if callback:
788+
self.channels[chan]['callback'] = callback
793789
payload = {'nonce': self.nonce}
794790
payload_encoded = _urlencode(payload)
795791
sign = _new(
@@ -799,22 +795,29 @@ def subscribe(self, chan):
799795

800796
self.socket.send(_dumps({
801797
'command': 'subscribe',
802-
'channel': chan,
798+
'channel': self.channels[chan]['id'],
803799
'sign': sign.hexdigest(),
804800
'key': self.key,
805801
'payload': payload_encoded}))
806802
else:
807-
self.socket.send(_dumps({'command': 'subscribe', 'channel': chan}))
803+
self.channels[chan]['sub'] = True
804+
if callback:
805+
self.channels[chan]['callback'] = callback
806+
self.socket.send(_dumps({'command': 'subscribe',
807+
'channel': self.channels[chan]['id']}))
808808

809809
def unsubscribe(self, chan):
810810
""" Sends the 'unsubscribe' command for <chan> """
811+
if isinstance(chan, int):
812+
chan = self._getChannelName(str(chan))
811813
# account chan?
812-
if chan in ['1000', 1000]:
814+
if chan == 'account':
813815
# sending commands to 'account' requires a key, secret and nonce
814816
if not self.key or not self.secret:
815817
raise PoloniexError(
816818
"self.key and self.secret needed for 'account' channel"
817819
)
820+
self.channels[chan]['sub'] = False
818821
payload = {'nonce': self.nonce}
819822
payload_encoded = _urlencode(payload)
820823
sign = _new(
@@ -824,39 +827,35 @@ def unsubscribe(self, chan):
824827

825828
self.socket.send(_dumps({
826829
'command': 'unsubscribe',
827-
'channel': chan,
830+
'channel': self.channels[chan]['id'],
828831
'sign': sign.hexdigest(),
829832
'key': self.key,
830833
'payload': payload_encoded}))
831834
else:
832-
self.socket.send(_dumps({'command': 'unsubscribe', 'channel': chan}))
835+
self.channels[chan]['sub'] = False
836+
self.socket.send(_dumps({'command': 'unsubscribe',
837+
'channel': self.channels[chan]['id']}))
833838

834-
def setCallback(self, chan, callback):
835-
""" Sets the callback function for <chan> """
836-
self.channels[chan]['callback'] = callback
837-
838-
def startws(self, subscribe=[]):
839+
def startws(self, subscribe=False):
839840
"""
840841
Run the websocket in a thread, use 'subscribe' arg to subscribe
841-
to a channel on start
842+
to channels on start
842843
"""
843844
self._t = Thread(target=self.socket.run_forever)
844845
self._t.daemon = True
845846
self._running = True
846847
# set subscribes
847-
for chan in self.channels:
848-
if self.channels[chan]['name'] in subscribe or chan in subscribe:
849-
self.channels[chan]['sub'] = True
848+
if subscribe:
849+
self.setSubscribes(**subscribe)
850850
self._t.start()
851851
self.logger.info('Websocket thread started')
852852

853-
854-
def stopws(self, wait=0):
853+
def stopws(self, wait=1):
855854
""" Stop/join the websocket thread """
856855
self._running = False
857856
# unsubscribe from subs
858857
for chan in self.channels:
859-
if self.channels[chan]['sub'] == True:
858+
if 'sub' in self.channels[chan] and self.channels[chan]['sub']:
860859
self.unsubscribe(chan)
861860
sleep(wait)
862861
try:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
read_md = lambda f: open(f, 'r').read()
44

55
setup(name='poloniexapi',
6-
version='0.5.6',
6+
version='0.5.7',
77
description='Poloniex API wrapper for Python 2.7 and 3 with websocket support',
88
long_description=read_md('README.md'),
99
long_description_content_type='text/markdown',

0 commit comments

Comments
 (0)