何も考えずにトレイルストップもどきを作ってみたのをちょっと改良
在庫を見つけたら価格の上昇に合わせてストップリミットオーダーを発行
リミット値は現在値の0.8%下に設定
リミット値は後戻りしないので0.8%以上上昇すれば勝ち?
使う場合は適当に変更してください
LINEで定時発報していますので動作中であれば、新たに購入して在庫ができれば動き続けるはず・・・
import time
from binance.client import Client # bainanceクライアント
from binance.websockets import BinanceSocketManager # BinanceにWebsocketで接続するのに必要
from binance.enums import * # ENUM定義
import datetime
import requests
import math
TRAIL_STOP_LIMIT = 0.992 # ストップリミット(トレイルストップコントロール中の最高値の99.2%を下回ったら即売り)
# Binance API TOKEN
PUBLIC = 'バイナンスから取得してください'
SECRET = 'バイナンスから取得してください'
# LINE API TOKEN
LINE = 'LINEから取得してください'
# LINE通知
def SendLineNotify(msg):
lineNotifyApi = 'https://notify-api.line.me/api/notify'
headers = {'Authorization': f'Bearer {LINE}'}
data = {'message': f'message: {msg}'}
requests.post(lineNotifyApi, headers = headers, data = data)
class TrailStop:
def __init__(self,dest,src):
self.calcLimit = 0.0 # 現在値に対しての常時演算
self.stopLimit = 0.0 # トレイルストップ値
self.minQty = 0.0 # 最小取引数
self.symbol = dest + src #通貨ペアのシンボルを作成 ex.BTC/USDT -> BTCUSDT
self.target = dest
self.src = src
# クライアント接続開始
self.connect()
# 最低取引数量計算
info = self.client.get_symbol_info(self.symbol) # 通貨ペアの情報を取得
self.minQty = float(info['filters'][2]['minQty']) # 基軸通貨の最低取引量
self.dp = len(str(int(1/self.minQty))) - 1 # 小数点位置を取得
# BTCのお値段確認
ticker = self.client.get_ticker(symbol='BTCUSDT')
btcPrice = float(ticker['lastPrice'])
# 今回の取引通貨ペアのお値段確認
ticker = self.client.get_ticker(symbol=self.symbol)
lastPrice = float(ticker['lastPrice'])
# 現物を確認
qty = float(self.GetQty())
self.qtyValue = qty # 現物の量を覚えておく
value = '{:.6g}'.format(qty)
self.spotStatus = False
# 通貨ベアの現在の状態調査用
self.minTradeQty = 10.3/ lastPrice
print('最低取引単位',self.minQty,"小数点桁数",self.dp,"最低取引数量",self.minTradeQty)
print("現物確認","qty:",value,"現在の価格:",lastPrice,"BTC Price:",btcPrice)
# オーダーブックを確認
res = self.GetOrders()
if len(res)!=0:
self.spotStatus = True
else:
# 10.3$以上現物があるかどうか。なかったら購入待ちで待機
if (qty * lastPrice) < self.minTradeQty:
print("購入待ち")
self.spotStatus = False
else:
msg = "トレイルストップコントロール開始しました:" + self.symbol
SendLineNotify(msg)
print("トレイルストップコントロール開始")
self.spotStatus = True
self.preValue = lastPrice # 前回の価格
self.preStopLimit = 0 #ストップリミット
self.nexttime005 = datetime.datetime.now() # 現物監視用タイマ
self.nexttime3600 = datetime.datetime.now() # 現物監視用タイマ
# バイナンスAPIサーバへ接続
def connect(self):
self.client = Client(api_key=PUBLIC, api_secret=SECRET)
# Websocketからのメッセージ
# 主にここで条件を定義して
def handle_message(self,msg):
self.price = float(msg['p']) # 現在の価格
# ストップ値計算
self.calcLimit = self.price * TRAIL_STOP_LIMIT
# 現在時刻
now = datetime.datetime.now()
# 演算された現在のストップ値が
if self.calcLimit > self.stopLimit and self.spotStatus == True:
self.stopLimit = self.calcLimit
print("リミット更新","現在の価格:",'{:.6g}'.format(self.price),"STOP LIMIT:",'{:.6g}'.format(self.stopLimit))
# 1分間のアクセス数に制限があるので5秒に一回程度にしておく
# 現物があるときは問合せを実行しない
if now >= self.nexttime005:
self.nexttime005 = datetime.datetime.now() + datetime.timedelta(seconds=5) # 次の判定タイミングをセット
# オーダーブックを確認
res = self.GetOrders()
if len(res)!=0:
for r in res:
side = r['side']
if side == 'SELL' and self.spotStatus == False:
self.spotStatus = True
else:
self.spotStatus = False
else:
self.spotStatus = False
# オーダーブックにもなかったら原物の在庫を確認する
if self.spotStatus == False:
# 現物の状態を確認
qty = float(self.GetQty())
self.qtyValue = qty # 現物の量を更新
#print("現物確認","qty:",qty,"現在の価格:",self.price)
# 現物が取引最低数量存在するかどうか判定
if qty > self.minTradeQty:
print("現物を発見")
print("トレイルストップコントロール開始")
self.spotStatus = True
else:
self.spotStatus = False
# 5秒前のストップリミットが前回値より低かったらオーダーを更新する
if self.spotStatus == True:
if self.preStopLimit < self.stopLimit:
self.stoplimitSell(self.stopLimit)
self.preStopLimit = self.stopLimit
# 前回値を保存
self.preValue = self.price
if now >= self.nexttime3600:
# BTCのお値段確認
ticker = self.client.get_ticker(symbol='BTCUSDT')
btcPrice = float(ticker['lastPrice'])
# 今回の取引通貨ペアのお値段確認
ticker = self.client.get_ticker(symbol=self.symbol)
lastPrice = float(ticker['lastPrice'])
self.nexttime3600 = datetime.datetime.now() + datetime.timedelta(seconds=3600) # 次の判定タイミングをセット
msg = 'トレイルコントロール中:'
msg += self.target
msg += '現在の価格:'
msg += '{:.6g}'.format(self.price)
msg += self.src
msg += '現在のリミット値:'
msg += '{:.6g}'.format(self.stopLimit)
SendLineNotify(msg)
'''未使用
# トレイルストップ トラップ発動判定
if self.price <= self.stopLimit and self.spotStatus ==True:
self.spotStatus = False
print("トレイルストップ")
self.Cancel()
self.marketSell()
print("現在の価格:",self.price,"STOP LIMIT:",self.stopLimit)
msg = "トレイルストップ:" + ""
'''
'''未使用
# 前回値との差分が1%以上で下がったら
if self.price <= self.preValue * 0.99 and self.spotStatus ==True:
self.spotStatus = False
print("相場急落 緊急売却")
self.Cancel()
self.marketSell()
print("現在の価格:",self.price,"STOP LIMIT:",self.stopLimit)
'''
# Websocketスタート
def Start(self):
self.bm = BinanceSocketManager(self.client)
self.conn_key = self.bm.start_trade_socket(self.symbol, self.handle_message)
self.bm.start()
# 開始したらループ
try:
while True:
pass
except KeyboardInterrupt:
# Ctrl-C を捕まえた!
print('interrupted!')
return
# コネクションクローズ
def Close(self):
if self.bm is None:
pass
else:
self.bm.stop_socket(conn_key)
self.bm.close()
# ストップリミット注文
def stoplimitSell(self,stopValue):
val = float(self.GetQty())
n = 2 # ETH/USDT通貨ペアだと小数点二桁
value = math.floor(stopValue * 10 ** n) / (10 ** n)
l = value + 0.01 # USDTの最小取引単位
limitPrice = math.floor(l * 10 ** n) / (10 ** n)
#limitPrice = str(round(stopValue + 0.01,2))
n = self.dp-1
qty = math.floor(float(self.Cancel()) * 10 ** n) / (10 ** n)
#qty = str(round(float(self.Cancel()),self.dp))
print("売却 StopLimit更新:","qty",qty,"limitPrice",limitPrice)
order = self.client.create_order(
symbol = self.symbol,
side = SIDE_SELL,
type = ORDER_TYPE_STOP_LOSS_LIMIT,
timeInForce = TIME_IN_FORCE_GTC,
quantity = str(qty),
price = str(value),
stopPrice = str(limitPrice)
)
# 売り飛ばし
def marketSell(self):
value = round(float(self.qtyValue)*0.99,self.dp)
qty = '{:6g}'.format(value)
self.spotStatus == False
if value * self.price < self.minTradeQty:
print("現物が最小取引数量を満たしません:","現物",qty,"最低取引数量",self.minTradeQty)
else:
print("売るよ",qty)
order = self.client.order_market_sell(symbol=self.symbol, quantity=qty)
if Isfloat(order['cummulativeQuoteQty']):
now_asset = float(order['cummulativeQuoteQty'])
print("売り価格",now_asset)
msg = "トレールストップリミット発動しました。"
SendLineNotify(msg)
msg="販売価格" + str(now_asset)
SendLineNotify(msg)
else:
msg = "売るの失敗しているかも。すぐに確認を"
SendLineNotify(msg)
# 購入
def MarketBuy(self):
qty = round(self.GetSrcQty(),self.dp)
# 買い注文(成行注文)
print("買うよ",qty)
sell_order = self.client.order_market_buy(symbol=self.symbol, quantity=qty)
now_asset = float(sell_order['cummulativeQuoteQty'])
print("買い価格",now_asset)
self.calcLimit = self.price * 0.99
# 使用可能在庫量の取得
def GetQty(self):
balance = self.client.get_asset_balance(asset=self.target)
return balance['free']
# 使用可能購入用通貨量の取得
def GetSrcQty(self):
basis = self.client.get_asset_balance(asset=self.src)
print(basis)
basisFree = float(basis['free'])
ticker = self.client.get_ticker(symbol=self.symbol)
lastPrice = float(ticker['lastPrice'])
print(basisFree,lastPrice)
qty = ((basisFree / lastPrice * 0.99))
return qty
# オーダーキャンセルして在庫量を返す。
def Cancel(self):
qty = "0.0"
res = self.GetOrders()
if len(res)!=0:
for r in res:
id = r['orderId']
result = self.client.cancel_order(
symbol=self.symbol,
orderId=id)
qty = r['origQty']
else:
qty = self.GetQty()
return qty
# オーダーブック確認
def GetOrders(self):
return self.client.get_open_orders(symbol=self.symbol)
def main():
pair1 = "ETH"
pair2 = "USDT"
cl = TrailStop(pair1,pair2)
cl.Start()
cl.Close()
print('終了')
if __name__ == "__main__":
main()
簡単な自動売買を組んで2時間ほど動かしたら5万ほど消えていたので
自動買いはとりあえず無し・・・
handle_messageの所に買い条件も入れたらなんとなく自動売買は作れてしまう
はまるポイントとしては通貨によって桁数が違うことと
バイナンスの手数料を考慮して金額を入れる部分ですね
ETH/USDTは小数点二桁だけどDOGE/USDTの通貨ペアだと小数点4桁だったりする
参考