Pycon 2019 @Shenzhen

mac2026-02-07  0

.  上一篇文章時間沒錯,2016年10月,一路向南。在金山的最後一篇博文,恰好也是向南回歸廣東的創業創意開始見起色的時間。然後就係回歸廣東,創業3年。   準確離職時間是17年吧,都已經覺得猴年馬月了,近期時間過得好像特別快,而回看離職那時,又覺得特別漫長,或許最近有點閒吧,閒得報了個PyCon的主題,演講了一篇《從Python開始錢賺錢》。   其實內容不是特別深奧,我也是特意簡單概括簡明扼要地講解了一下,說實話,是個有花了10個工作日精心設計了的演講,內容水中點進階,簡單得嚟又要引起疑問。最後還是說了下人生大道理,知行合一。不是說真的教大家怎麼賺錢,好吧,我的確也能盈利,更重要的是,希望可以讓大家感受下知行合一的過程,感受下自己編程的初心,感受下這一份樂趣,希望大家工作的路上不要這麼無聊地996了。   這也權作我這麼一個從Python走出社會的人,對Python社區的一份回報吧。   至於創業沒啥好說的,挺成功的,嗯,餓不死了,後面應該就學怎樣理財,讓我的10萬資本變100萬了:)。

PPT-PDF版本

#!/usr/bin/python # -*- coding: utf-8 -*- """ One Trader Trade some pairs once """ __author__ = 'Zagfai' __date__ = '2019-02' import datetime import math import asyncio import aiohttp import json import logging import time import requests import binance.client as binanceclient import binance.exceptions as binanceexceptions from collections import OrderedDict TRADE_PAIRS = { # PAIRX: (upper_bound, lower_bound, capital, min_buy/sell_precise) 'ETHUSDT': (350, 100, 3000, 4), 'BNBUSDT': (42, 12, 3000, 2), 'XMRUSDT': (120, 50, 2400, 4), } MIN_TRADE = 0.48 # min trade percentage of volatility class MixClient(): proxies = {} # proxies = {'https': 'http://192.168.3.57:1080'} api_key = "" # NOQA api_secret = "" # NOQA def __init__(self, k, s): self.api_key = k self.api_secret = s self.c = binanceclient.Client( self.api_key, self.api_secret, {'proxies': self.proxies}) def get_valuable_balance(self): info = self.c.get_account() balance = {i['asset']: round( float(i['free']), TRADE_PAIRS.get("%sUSDT", (0, 0, 0, 2))[3]) for i in info['balances'] if float(i['free']) > 0.0001} return balance def get_open_orders(self): orders = self.c.get_open_orders() return orders def get_my_last_trade(self, symbol): trades = self.c.get_my_trades(symbol=symbol, limit=1) return (round(float(trades[0]['price']), 7), datetime.datetime.utcfromtimestamp(trades[0]['time']/1000)) def get_orderbook_price(self, symbol): # trades = self.c.get_recent_trades(symbol='LTCUSDT', limit=1) ticker = self.c.get_orderbook_ticker(symbol=symbol) return (float(ticker['askPrice']), float(ticker['bidPrice'])) def get_orderbook_prices(self): ticker = self.c.get_orderbook_tickers() return (float(ticker['askPrice']), float(ticker['bidPrice'])) def get_all_tickers(self): return {i['symbol']: float(round(float(i['price']), 7)) for i in self.c.get_all_tickers()} def order(self, **params): return self.c.create_order(**params) def get_least_volatility(self, pair): klines = self.c.get_klines( symbol=pair, interval=binanceclient.Client.KLINE_INTERVAL_1HOUR, limit=168) blines = list(reversed([ round((float(i[2])-float(i[3]))/float(i[3]) * 100, 2) for i in klines])) last = blines[0] * 0.8 last6h = sorted(blines[:6])[1] last24h = sorted(blines[:24])[24//6] sf2 = sum(blines[22::24])/len(blines[22::24]) sf1 = sum(blines[23::24])/len(blines[23::24]) sf0 = sum(blines[::24])/len(blines[::24]) sf = (sf2 + sf1 + sf0) / 3 * 0.8 # future two hours last week weighted = last * 0.2 + last6h * 0.3 + last24h * 0.2 + sf * 0.3 if weighted > 1.5: weighted = 1.5 return round(weighted, 2) class Counter(): def parts(self, upper, lower, delta): return math.log(upper / lower) / math.log(delta) def one_part_cap(self, cap, upper, lower, delta): return cap / self.parts(upper, lower, delta) def should_hold(self, cap, upper, lower, nowprice): if nowprice <= lower or nowprice >= upper: return None delta = nowprice / lower shares_to_hold = cap - self.one_part_cap(cap, upper, lower, delta) avg_bought_price = (nowprice + upper) / 2 return shares_to_hold / avg_bought_price * nowprice class LoggingBeautiful(): def assets_balance(tickers, balances, pairs): sum_usdt = 0 for symb in balances: pair = '%sUSDT' % symb if symb == 'USDT': sum_usdt += balances[symb] elif symb in balances and pair in tickers: sum_usdt += balances[symb] * tickers[pair] logging.info( "Current assets balance: %s %s" % (int(sum_usdt), json.dumps(OrderedDict(sorted(balances.items()))))) def init_hold(tickers, pairs): logstrs = [] for pi in pairs: pa = pairs[pi] s = Counter().should_hold(pa[2], pa[0], pa[1], tickers[pi]) if not s: s = 0 sh = round(s / tickers[pi], 2) logstrs.append("%s: %s; " % (pi, sh)) logging.info("Should hold: " + ' '.join(logstrs)) def market_order_info(price_info): logstrs = [] for pi in price_info.values(): price = pi.get('price') logstrs.append("%s (%s %s%s%% %s($%s));" % ( pi['pair'], price, pi.get('trade_mark') == "BUY" and '-' or '+', round(pi.get('pc_delta_pct', 0), 2), pi.get('quan', 0), round(pi.get('quan', 0)*pi.get('price', 0), 2))) logging.info("Market: " + ' '.join(sorted(logstrs))) def minvol(pairs): mintradedollar = 10 minvols = [] logstrs = [] for pair in pairs: pp = pairs[pair] a = mintradedollar*math.log(pp[0]/pp[1])/pp[2] minvol = (math.pow(math.e, a) - 1) * 100 minvols.append(minvol) logstrs.append("%s: %s%%; " % (pair, round(minvol, 2))) logging.info("Lowest trade volatility: " + ' '.join(logstrs)) if any(i > MIN_TRADE for i in minvols): logging.error("Error start with too high minimal volatility.") logging.error("Should lower that MIN_TRADE.") exit() def least_volatility(vols): logging.info("Aim volatility: %s" % (json.dumps(OrderedDict(sorted(vols.items()))))) def trade_one(c, pi, last_trade_price, balance, least_vola, last_trade_time): pair = pi['pair'] pi['upper'], pi['lower'], pi['cap'], pi['minbs'] = TRADE_PAIRS[pair] pi['ask'], pi['bid'] = c.get_orderbook_price(pair) if last_trade_price < pi['bid']: pi['price'] = pi['bid'] elif last_trade_price > pi['ask']: pi['price'] = pi['ask'] else: pi['price'] = round((pi['ask']+pi['bid'])/2, 7) delta_price = abs(last_trade_price-pi['price']) pi['pc_delta_pct'] = delta_price / min(last_trade_price, pi['price']) * 100 s = Counter().should_hold(pi['cap'], pi['upper'], pi['lower'], pi['price']) if not s: return False pi['should_hold'] = round(s, 2) pi['quan'] = round(pi['should_hold']/pi['price'] - balance, pi['minbs']) pi['trade_mark'] = pi['quan'] > 0 and 'BUY' or 'SELL' timedelta = abs(datetime.datetime.now() - last_trade_time) traded_inhour = timedelta < datetime.timedelta(0, 3600) traded_in1min = timedelta < datetime.timedelta(0, 60) if pi['pc_delta_pct'] < MIN_TRADE: # No profitable return False if pi['pc_delta_pct'] < least_vola and traded_inhour: return False if pi['pc_delta_pct'] < 2.0 and traded_in1min: return False if abs(pi['quan']*pi['price']) <= 10: # Minimax trade $10 return False if pi['ask'] <= pi['lower'] or pi['bid'] <= pi['lower'] or \ pi['ask'] >= pi['upper'] or pi['bid'] >= pi['upper']: return False order = c.order( symbol=pi['pair'], side=pi['trade_mark'], type='MARKET', quantity=abs(pi['quan'])) logging.info("Trade: %s %s %s as $%s (hold $%s) %s%%" % (pi['pair'], pi['trade_mark'], pi['quan'], round(pi['quan']*pi['price'], 2), pi['should_hold'], round(pi['pc_delta_pct'], 2))) logging.info("OrderDetail: %s" % str(order)) time.sleep(2) return True def run(c): loop = 1 interval_sec = 3 last_trade_price_dict = {} last_trade_time_dict = {} least_vola_dict = {} balances = {} ordered = False while True: # every 1.5 hours recount least volatility if loop % (1.5*60*60//interval_sec) == 1: least_vola_dict = { i: c.get_least_volatility(i) for i in TRADE_PAIRS} LoggingBeautiful.least_volatility(least_vola_dict) # reset blnc & ltp or ordered if loop % (3*60//interval_sec) == 1 or ordered: balances = c.get_valuable_balance() for i in TRADE_PAIRS: tinfo = c.get_my_last_trade(i) last_trade_price_dict[i], last_trade_time_dict[i] = tinfo # print assets and needfix if loop % (30*60//interval_sec) == 1 or ordered: LoggingBeautiful.assets_balance( c.get_all_tickers(), balances, TRADE_PAIRS) # check existed order if ordered and c.get_open_orders(): logging.error("Order not finish.") time.sleep(5) break ordered = False price_info = {} for pair in TRADE_PAIRS: if not pair.endswith('USDT'): logging.error("Not support pairs base on other than USDT.") exit() pi = {'pair': pair, 'symb': pair[:-4]} price_info[pair] = pi ltp = last_trade_price_dict[pair] ltt = last_trade_time_dict[pair] if trade_one(c, pi, ltp, balances.get(pi['symb'], 0), least_vola_dict[pair], ltt): ordered = True if loop % (10*60//interval_sec) == 1 or ordered: LoggingBeautiful.market_order_info(price_info) time.sleep(interval_sec) loop += 1 if __name__ == "__main__": logging.basicConfig(format='%(asctime)s [%(levelname)s] %(message)s', level=logging.INFO) with open('/etc/binanceapikey') as f: key = f.readline().strip() sec = f.readline().strip() logging.info("Trade started.") LoggingBeautiful.minvol(TRADE_PAIRS) LoggingBeautiful.init_hold( MixClient(key, sec).get_all_tickers(), TRADE_PAIRS) time.sleep(3) while True: try: run(MixClient(key, sec)) except (requests.exceptions.ProxyError, requests.exceptions.ReadTimeout, requests.exceptions.ConnectTimeout, requests.exceptions.ConnectionError, requests.exceptions.SSLError, requests.exceptions.RetryError, requests.packages.urllib3.exceptions.ProtocolError, binanceexceptions.BinanceAPIException) as e: logging.warn("Restarted trade because of %s" % str(e)) time.sleep(5) except OSError as e: if 'OSError("(104,' in str(e): logging.warn("Restarted trade because of %s" % str(e)) time.sleep(2) else: logging.error("UNKNOWN ERROR OSError: %s" % repr(e)) break except SystemExit: logging.error("System exit by SystemExit except.") break except Exception as e: logging.error("UNKNOWN ERROR: %s" % repr(e)) break
最新回复(0)