371 lines
12 KiB
Python
371 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import sys
|
|
from collectors.collector import Collector
|
|
import logging
|
|
|
|
# import API
|
|
from lib.pybinancefutures import *
|
|
|
|
# curl
|
|
import json
|
|
import datetime, time
|
|
import requests
|
|
from pandas import DataFrame
|
|
import pandas as pd
|
|
from bs4 import BeautifulSoup
|
|
|
|
from db import DB
|
|
|
|
# access
|
|
# DPob3MlV51nb55D6OovjKTNRiyoiMWihX2phcunUNxI73Z7gSyo2ALX87dxcmuXB
|
|
# secret
|
|
# qgd5YHf4TiWvD8KjOL1qoPz9QX354mYMIoQ6FBt5VCv1tswQq3X6eGaFFrHZ7a7a
|
|
|
|
angel = 'power'
|
|
|
|
class Binance(Collector):
|
|
def __init__(self, access=None, secret=None):
|
|
self.remaining_req = {}
|
|
self._heaeder = {}
|
|
self._mathod = 'get'
|
|
self._finance = 'crypto' # 금융 종목
|
|
self._name = 'binance' # 거래소 이름
|
|
self.leverage = 1
|
|
self._standard_price = 0
|
|
self._temp_api_url = 'https://fapi.binance.com/fapi/v1/' # 임시 api 주소
|
|
|
|
self._market = MarketData()
|
|
|
|
if secret is not None:
|
|
self._api = Client(access, secret)
|
|
|
|
self._api_url = self._temp_api_url
|
|
|
|
self._set_allow_items()
|
|
|
|
# get filtered markets
|
|
self.markets = self._load_markets() # 테스트때 주석
|
|
|
|
'''
|
|
For Cron Funtions
|
|
'''
|
|
# 바이낸스 기준 시총 정의
|
|
def _get_standard_trade_price(self, markets=None):
|
|
p_list = []
|
|
for m in self._market.ticker_price_24h():
|
|
p = m['lastPrice']
|
|
v = m['volume']
|
|
|
|
p_list.append(float(p)*float(v))
|
|
|
|
p_list.sort(reverse=True)
|
|
|
|
return float(p_list[1] - p_list[1] * 0.01)
|
|
|
|
def _return_list_filted_markets(self):
|
|
filtered_list = []
|
|
|
|
for m in self._market.ticker_price_24h():
|
|
s = m['symbol']
|
|
p = m['lastPrice']
|
|
v = m['volume']
|
|
|
|
f_if = False
|
|
|
|
# 리플 제외
|
|
if 'XRP' in s:
|
|
continue
|
|
|
|
# 허용 아이템 조건
|
|
for i in self.allow_items:
|
|
if i == s.replace('USDT', ''):
|
|
f_if = True
|
|
|
|
# 24시간 거래금액 및 종목 가격 제한 조건
|
|
# f_if = float(p) * float(v) > self._standard_price \
|
|
# and float(p) > float(100)
|
|
|
|
if f_if:
|
|
item = {
|
|
'market': str(s),
|
|
'acc_trade_price_24h': float(p) * float(v), # 거래 총 금액
|
|
'acc_trade_volume_24h': float(v), # 거래량
|
|
}
|
|
filtered_list.append(item)
|
|
|
|
return filtered_list
|
|
|
|
def _save_market_list_to_db(self, markets):
|
|
for m in markets:
|
|
self._save_item_data_to_item_table(self._finance, # 금융
|
|
self._name, # 거래소
|
|
m['market'], # 종목
|
|
m['acc_trade_price_24h'], # 24시간 거래 금액
|
|
m['acc_trade_volume_24h'] # 24시간 거래량
|
|
)
|
|
|
|
def _load_markets(self):
|
|
# DB에서 종목이 없을 경우 아래 로직 실행
|
|
try:
|
|
markets = self._return_list_filted_markets()
|
|
|
|
# set exchange min marget cap(시총)
|
|
self._standard_price = self._get_standard_trade_price(markets)
|
|
|
|
# 거래소 기본정보 DB 저장 (거래 기준가, api_url 등)
|
|
self._add_exchange_info_to_db(self._finance, self._name, self._standard_price, self._api_url)
|
|
|
|
# 종목 리스트 DB 저장
|
|
self._save_market_list_to_db(markets)
|
|
|
|
return markets
|
|
|
|
except Exception as e:
|
|
logging.error(e)
|
|
return self._load_markets()
|
|
# raise Exception(e) # for dev
|
|
|
|
def save_current_min_data(self, market):
|
|
time_type = 'minute'
|
|
data = self.get_history_data(market, time_type)
|
|
self._save_to_db_from_collectors_dataframe(self.finance, self._name, market, data, time_type)
|
|
|
|
def save_current_hour_data(self, market):
|
|
time_type = 'hour'
|
|
data = self.get_history_data(market, time_type)
|
|
self._save_to_db_from_collectors_dataframe(self.finance, self._name, market, data, time_type)
|
|
|
|
def save_current_day_data(self, market):
|
|
time_type = 'day'
|
|
data = self.get_history_data(market, time_type)
|
|
self._save_to_db_from_collectors_dataframe(self.finance, self._name, market, data, time_type)
|
|
|
|
def get_history_data(self, symbol="BTCUSDT", interval="hour", rm_last=True):
|
|
int2type = {
|
|
"day": "1d",
|
|
"hour12": "12h",
|
|
"hour6": "6h",
|
|
"hour4": "4h", # custom hour
|
|
"hour": "1h",
|
|
"minute30": "30m",
|
|
"minute15": "15m",
|
|
"minute10": "15m",
|
|
"minute5": "5m",
|
|
"minute3": "3m",
|
|
"minute": "1m",
|
|
}
|
|
# set symbol
|
|
self._market.symbol = symbol
|
|
|
|
data = self._market.candles_data(int2type[interval], None, None, 1500)
|
|
columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Close_Time',
|
|
'Quote_Asset_Volume', 'Number_of_Trades', 'Taker_Buy_Base_Asset_Volume',
|
|
'Taker_Buy_Quote_Asset_Volume', 'Ignore']
|
|
|
|
df = DataFrame(data, columns=columns)
|
|
self.data_columns_init(df)
|
|
|
|
if not df.empty:
|
|
# Convert timestamp to date format
|
|
dates = [datetime.datetime.fromtimestamp(int(d) / 1000).strftime("%Y-%m-%d %H:%M:%S")
|
|
for d in df['Date'].values]
|
|
# df.loc[:, 'Date'] = df['Date']\
|
|
# .apply(lambda d: datetime.datetime.fromtimestamp(int(d)/1000).strftime("%Y-%m-%d %H:%M:%S"))
|
|
|
|
# Set needs columns
|
|
df = df[['Open', 'High', 'Low', 'Close', 'Volume']]
|
|
|
|
# Remove last candle
|
|
if rm_last:
|
|
df = df[:-1]
|
|
|
|
df.loc[:, symbol] = pd.Series(dates)
|
|
df = df.set_index(symbol)
|
|
|
|
# return df
|
|
return df.astype(float)
|
|
else:
|
|
return None
|
|
|
|
'''
|
|
For Trade Funtions
|
|
'''
|
|
|
|
def get_current_price(self, symbol):
|
|
self._market.symbol = symbol
|
|
recent_price = self._market.ticker_price_symbol(symbol)
|
|
|
|
if recent_price:
|
|
return recent_price[0]['price']
|
|
|
|
return None
|
|
|
|
def get_position_info(self):
|
|
return self._api.position_info()
|
|
|
|
def get_last_price_from_orderbook(self, symbol, position, cnt=0):
|
|
if cnt > 10:
|
|
return None, None
|
|
|
|
self._market.symbol = symbol
|
|
order_book = self._market.ticker_orderbook_symbol(symbol)
|
|
cnt += 1
|
|
|
|
for b_d in order_book:
|
|
if b_d['symbol'] in symbol:
|
|
if position == 'long':
|
|
return float(b_d['bidPrice'])
|
|
elif position == 'short':
|
|
return float(b_d['askPrice'])
|
|
|
|
time.sleep(1)
|
|
return self.get_last_price_from_orderbook(symbol, position, cnt)
|
|
|
|
def get_trading_fee(self):
|
|
return float(0.02)
|
|
|
|
# get_position_balance
|
|
def get_position_balance(self, symbol='USDT'):
|
|
time.sleep(1)
|
|
|
|
for b in self._api.position_info():
|
|
if str(b['symbol']) == str(symbol):
|
|
return float(abs(float(b['positionAmt'])))
|
|
|
|
return False
|
|
|
|
def get_balance(self, symbol='USDT'):
|
|
for b in self._api.balance():
|
|
if str(b['symbol']) == str(symbol):
|
|
return self.cal_ceil(b['positionAmt'], 5)
|
|
return 0
|
|
|
|
def get_now_amount(self, symbol):
|
|
res = {}
|
|
|
|
for b in self._api.balance():
|
|
if str(b['asset']) == 'USDT':
|
|
res['KRW'] = '{:.8f}'.format(float(b['withdrawAvailable']))
|
|
elif str(b['asset']) in symbol:
|
|
res[b['asset']] = '{:.8f}'.format(float(b['withdrawAvailable']))
|
|
|
|
return res
|
|
|
|
def get_all_seeds(self):
|
|
for b in self._api.balance():
|
|
if b['asset'] == 'USDT':
|
|
return '{:.8f}'.format(float(b['balance']))
|
|
|
|
# BinanceFuturesPy
|
|
def order_long(self, symbol, order=None, cnt=0):
|
|
self._api.symbol = symbol
|
|
|
|
# 레버리지 설정
|
|
self._api.change_leverage(self.leverage)
|
|
|
|
# 이전 주문 취소
|
|
self.order_cancel(order)
|
|
|
|
target_price = self.get_last_price_from_orderbook(symbol, 'long')
|
|
seeds = float(self.get_now_amount(symbol)['KRW'])
|
|
amount = self.get_position_balance(symbol)
|
|
|
|
if not amount > 0:
|
|
amount = self.cal_ceil(seeds / target_price, 5)*int(self.leverage)
|
|
|
|
if not amount > 0:
|
|
return False, None
|
|
|
|
if cnt > 100:
|
|
# 시장가 매수
|
|
order = self._api.new_order(side='BUY',
|
|
quantity=amount,
|
|
orderType='MARKET')
|
|
|
|
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
|
else:
|
|
# 지정가 매수
|
|
order = self._api.new_order(side='BUY',
|
|
quantity=amount,
|
|
price=target_price,
|
|
orderType='LIMIT',
|
|
timeInForce='GTC')
|
|
|
|
if order is None or 'msg' in order:
|
|
print('주문 오류 -', order['msg'])
|
|
return False, None
|
|
|
|
time.sleep(2)
|
|
|
|
ordered = self._api.query_order(order['orderId'])
|
|
|
|
if ordered['origQty'] != ordered['executedQty']:
|
|
cnt += 1
|
|
|
|
return self.order_long(symbol, order, cnt)
|
|
|
|
seeds = float(self.get_now_amount(symbol)['KRW'])
|
|
# seeds = self.get_now_amount(symbol)['KRW']
|
|
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
|
|
|
def order_short(self, symbol, order=None, cnt=0):
|
|
# for test
|
|
# return True, {'symbol': symbol, 'target_price': 8952000, 'amount': 1, 'seeds': 22000}
|
|
|
|
self._api.symbol = symbol
|
|
|
|
# 레버리지 설정
|
|
self._api.change_leverage(self.leverage)
|
|
|
|
# 이전 주문 취소
|
|
self.order_cancel(order)
|
|
|
|
target_price = self.get_last_price_from_orderbook(symbol, 'short')
|
|
seeds = float(self.get_now_amount(symbol)['KRW'])
|
|
amount = self.get_position_balance(symbol)
|
|
|
|
if not amount > 0:
|
|
amount = self.cal_ceil(seeds / target_price, 5)*int(self.leverage)
|
|
|
|
if not amount > 0:
|
|
return False, None
|
|
|
|
if cnt > 100:
|
|
# 시장가 매도
|
|
self._api.new_order(side='SELL',
|
|
quantity=amount,
|
|
orderType='MARKET')
|
|
|
|
seeds = self.get_now_amount(symbol)['KRW']
|
|
|
|
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
|
else:
|
|
order = self._api.new_order(side='SELL',
|
|
quantity=amount,
|
|
price=target_price,
|
|
orderType='LIMIT',
|
|
timeInForce='GTC')
|
|
|
|
if order is None or 'msg' in order:
|
|
print('주문 오류 -', order['msg'])
|
|
return False, None
|
|
|
|
time.sleep(2)
|
|
|
|
ordered = self._api.query_order(order['orderId'])
|
|
|
|
if ordered['origQty'] != ordered['executedQty']:
|
|
cnt += 1
|
|
return self.order_short(symbol, order, cnt)
|
|
|
|
seeds = float(self.get_now_amount(symbol)['KRW'])
|
|
# seeds = self.get_now_amount(symbol)['KRW']
|
|
return True, {'symbol': symbol, 'target_price': target_price, 'amount': amount, 'seeds': seeds}
|
|
|
|
def order_cancel(self, order=None):
|
|
if order is not None:
|
|
return self._api.cancel_order(order['orderId'])
|
|
|
|
return False
|