当前没有日志.+
序列 | +说明 | +状态 | +操作 | +
---|
当前没有日志.+
diff --git a/.gitignore b/.gitignore
index 0061db5f6..a673aae09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -176,7 +176,6 @@ plugins/op_auth
plugins/l2tp
plugins/openlitespeed
plugins/tamper_proof
-plugins/cryptocurrency_trade
plugins/zimg
plugins/bk_demo
plugins/mail
diff --git a/plugins/cryptocurrency_trade/README.md b/plugins/cryptocurrency_trade/README.md
new file mode 100644
index 000000000..8ec1d9a27
--- /dev/null
+++ b/plugins/cryptocurrency_trade/README.md
@@ -0,0 +1,78 @@
+# plugins_cryptocurrency_trade
+
+- https://docs.ccxt.com/en/latest/install.html#python-proxies
+
+数字货币量化交易插件
+
+```
+cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
+
+python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
+
+miniconda3
+
+
+
+```
+
+# 免费TV策略
+https://cn.tradingview.com/scripts/?script_type=strategies
+
+
+### tradingview pine 指标
+```
+// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
+// © TenCloud
+
+//@version=5
+indicator("第一个指标",shorttitle = "开心吧")
+
+ema10 = ta.ema(close, 25)
+ema100 = ta.ema(close, 100)
+l1=plot(ema10, color=color.red,title = "ema10")
+l2=plot(ema100,color = color.green, title = "ema100")
+
+
+buy = ta.crossover(ema10,ema100)
+sell = ta.crossunder(ema10,ema100)
+
+plotchar(buy, text = "buy", color=color.green)
+plotchar(sell, text="sell", color=color.red)
+
+l_color = ema10>ema100 ? color.green:color.red
+
+fill(l1,l2,color=color.new(l_color,70))
+
+```
+
+### tradingview pine 策略
+```
+// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
+// © TenCloud
+
+//@version=5
+strategy("第一个指标",shorttitle = "开心吧", overlay = true, initial_capital = 1000)
+
+ema10 = ta.ema(close, 25)
+ema100 = ta.ema(close, 100)
+l1=plot(ema10, color=color.red,title = "ema10")
+l2=plot(ema100,color = color.green, title = "ema100")
+
+
+buy = ta.crossover(ema10,ema100)
+sell = ta.crossunder(ema10,ema100)
+
+if buy
+ strategy.entry("long1", strategy.long,1)
+
+if sell
+ strategy.close("long1", qty_percent = 100,comment = "平仓多单")
+// plotchar(buy, text = "buy", color=color.green)
+// plotchar(sell, text="sell", color=color.red)
+
+// l_color = ema10>ema100 ? color.green:color.red
+
+// fill(l1,l2,color=color.new(l_color,70))
+
+
+```
\ No newline at end of file
diff --git a/plugins/cryptocurrency_trade/ccxt/public_data/data.py b/plugins/cryptocurrency_trade/ccxt/public_data/data.py
new file mode 100644
index 000000000..bdd0b1aec
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/public_data/data.py
@@ -0,0 +1,555 @@
+import ccxt
+
+
+from datetime import datetime
+import time
+import sys
+import json
+import os
+import glob
+import threading
+from pprint import pprint
+
+
+# print(os.getcwd())
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py run
+# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
+
+# 查看支持的交易所
+# print(ccxt.exchanges)
+
+# 代理设置
+# exchange = ccxt.poloniex({
+# 'proxies': {
+# 'http': 'http://127.0.0.1:1088'
+# },
+# })
+
+# exchange = ccxt.poloniex()
+exchange = ccxt.okex()
+
+
+def getPluginName():
+ return 'cryptocurrency_trade'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ log_file = getServerDir() + '/logs/datasource.log'
+ mw.writeFileLog(log_str, log_file, 1 * 1024 * 1024)
+ return True
+
+
+def isSetDbConf():
+ data = getConfigData()
+ if 'db' in data:
+ return True
+ return False
+
+
+def beforeDate(day=360):
+ dd = time.time() - day * 86400
+ d = datetime.fromtimestamp(dd)
+ day = d.strftime("%Y-%m-%d")
+ return day
+
+
+def getTextTimeShow(time):
+ d = datetime.fromtimestamp(time)
+ day = d.strftime("%Y-%m-%d %H:%M:%S")
+ return day
+
+
+def isSqlError(mysqlMsg):
+ # 检测数据库执行错误
+ mysqlMsg = str(mysqlMsg)
+ if "MySQLdb" in mysqlMsg:
+ return mw.returnJson(False, 'MySQLdb组件缺失!
进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
+ if "2002," in mysqlMsg:
+ return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
+ if "2003," in mysqlMsg:
+ return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
+ if "using password:" in mysqlMsg:
+ return mw.returnJson(False, '数据库密码错误')
+ if "1045" in mysqlMsg:
+ return mw.returnJson(False, '连接错误!')
+ if "SQL syntax" in mysqlMsg:
+ return mw.returnJson(False, 'SQL语法错误!')
+ if "Connection refused" in mysqlMsg:
+ return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
+ if "1133" in mysqlMsg:
+ return mw.returnJson(False, '数据库用户不存在!')
+ if "1007" in mysqlMsg:
+ return mw.returnJson(False, '数据库已经存在!')
+ return None
+
+
+def pMysqlDb():
+ # pymysql
+ db = mw.getMyORM()
+ data = getConfigData()
+ db_data = data['db']
+ db.setHost(db_data['db_host'])
+ db.setPort(db_data['db_port'])
+ db.setUser(db_data['db_user'])
+ db.setPwd(db_data['db_pass'])
+ db.setDbName(db_data['db_name'])
+ return db
+
+
+def makeInsertSql(table, item):
+ sql = "insert into " + table
+ keyStr = '('
+ valueStr = ' values('
+ for i in item:
+ # print i, item[i]
+ keyStr += '`' + str(i) + '`,'
+ valueStr += "'" + str(item[i]) + "',"
+
+ keyStrLen = len(keyStr)
+ keyStr = keyStr[0:keyStrLen - 1]
+ keyStr += ') '
+
+ valueStrLen = len(valueStr)
+ valueStr = valueStr[0:valueStrLen - 1]
+ valueStr += ') '
+
+ sql += keyStr
+ sql += valueStr
+
+ return sql
+
+
+def makeReplaceSql(table, item):
+ sql = "replace into " + table
+ keyStr = '('
+ valueStr = ' values('
+ for i in item:
+ # print i, item[i]
+ keyStr += '`' + str(i) + '`,'
+ valueStr += "'" + str(item[i]) + "',"
+
+ keyStrLen = len(keyStr)
+ keyStr = keyStr[0:keyStrLen - 1]
+ keyStr += ') '
+
+ valueStrLen = len(valueStr)
+ valueStr = valueStr[0:valueStrLen - 1]
+ valueStr += ') '
+
+ sql += keyStr
+ sql += valueStr
+
+ return sql
+
+
+def makeUpdateSql(table, item, mid):
+ sql = "update " + table + " set "
+ keyStr = ''
+ for i in item:
+ keyStr += '`' + str(i) + '`=' + "'" + item[i] + "',"
+
+ keyStrLen = len(keyStr)
+ keyStr = keyStr[0:keyStrLen - 1]
+ sql += keyStr
+ sql += " where id = '" + str(mid) + "'"
+ return sql
+
+
+def dataToDb(table_name, data):
+ pdb = pMysqlDb()
+ for i in data:
+ rdata = {
+ 'addtime': int(i[0] / 1000),
+ 'open': i[1],
+ 'high': i[2],
+ 'low': i[3],
+ 'close': i[4],
+ 'vol': i[5],
+ }
+
+ # sql = """SELECT id FROM %s where addtime='%s' LIMIT 1""" % (
+ # table_name, rdata['addtime'])
+ # fdata = pdb.query(sql)
+ # if fdata:
+ # print(table_name + ":" + str(rdata['addtime']) + ", old to db ok")
+ # else:
+ # isql = makeReplaceSql(table_name, rdata)
+ # r = pdb.execute(isql)
+ # print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
+
+ isql = makeReplaceSql(table_name, rdata)
+ r = pdb.execute(isql)
+ # print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
+ return True
+
+
+def makeTableName(input_type="btc", input_tf="1m"):
+ table_name = "ct_%s_%s" % (input_type, input_tf)
+ return table_name
+
+
+def isHasTable(input_type="btc", input_tf="1m"):
+ pdb = pMysqlDb()
+
+ # input_type = 'btc'
+ # input_tf = '1m'
+ table_name = makeTableName(input_type, input_tf)
+ mtable = pdb.query("show tables like '%s'" % (table_name,))
+ if len(mtable) != 0:
+ return True
+ return False
+
+
+def createSql(input_type="btc", input_tf="1m"):
+ pdb = pMysqlDb()
+
+ # input_type = 'btc'
+ # input_tf = '1m'
+ table_name = makeTableName(input_type, input_tf)
+
+ mtable = pdb.query("show tables like '%s'" % (table_name,))
+ if len(mtable) != 0:
+ return True
+
+ sql_tpl = getPluginDir() + "/conf/create.sql"
+ content = mw.readFile(sql_tpl)
+ content = content.replace("xx1", input_type)
+ content = content.replace("xx2", input_tf)
+
+ res = pdb.execute(content)
+ # pprint(res)
+ return True
+
+
+def getDataFetch(symbol, start_time="2023-3-1", input_tf="1m", limit=500):
+ start_time = datetime.strptime(start_time, '%Y-%m-%d')
+ end_time = datetime.strptime(end_time, '%Y-%m-%d')
+
+ start_time_stamp = int(time.mktime(start_time.timetuple())) * 1000
+ data = exchange.fetch_ohlcv(symbol, timeframe=input_tf,
+ since=start_time_stamp, limit=limit)
+ return data
+
+
+def getPointData(symbol, start_time=1677713220000, input_tf="1m", limit=500):
+ data = exchange.fetch_ohlcv(
+ symbol, timeframe=input_tf, since=start_time, limit=limit)
+ return data
+
+
+def toUnixTimeSecond(tf="1m"):
+ if tf.find("m") > -1:
+ v = int(tf.replace("m", ''))
+ return v * 60
+
+ if tf.find("h") > -1:
+ v = int(tf.replace("h", ''))
+ return v * 3600
+
+ if tf.find("d") > -1:
+ v = int(tf.replace("d", ''))
+ return v * 86400
+ return 0
+
+
+def findAndUpdateData(tag, input_tf="1m", start_time="2023-1-1", limit=300):
+ pdb = pMysqlDb()
+ table_name = makeTableName(tag, input_tf)
+ sql = 'SELECT addtime FROM ' + table_name + ' order by addtime desc LIMIT 1'
+ qdata = pdb.query(sql)
+
+ start_time = datetime.strptime(start_time, '%Y-%m-%d')
+ start_time = int(time.mktime(start_time.timetuple())) * 1000
+ if len(qdata) != 0:
+ start_time = int(qdata[0]['addtime']) * 1000
+
+ pre_time = toUnixTimeSecond(input_tf) * 5 * 1000
+ start_time = start_time - pre_time
+ # pprint(start_time)
+ symbol = tag.upper() + "/USDT"
+ data = getPointData(symbol, start_time, input_tf, limit)
+
+ # pprint(data)
+ # print("------------lini===========")
+ # pprint(data[1:])
+
+ if len(qdata) == 1:
+ data = data[1:]
+
+ dataToDb(table_name, data)
+
+ now = time.strftime("%m/%d %H:%M:%S")
+ data_len = len(data)
+ msg_head = now + "|虚拟币:" + tag + ",tf:" + \
+ input_tf + "!,总数:" + str(data_len)
+ writeLog(msg_head)
+ if data_len > 0:
+ # print("new data time", data[data_len - 1][0])
+ dt = getTextTimeShow(data[data_len - 1][0] / 1000)
+ msg = now + "|最新日期:" + dt
+ writeLog(msg)
+
+ table_name = makeTableName(tag, input_tf)
+
+ sql = "SELECT addtime FROM " + table_name + \
+ " order by addtime desc limit 10000,1"
+ qdata = pdb.query(sql)
+
+ if len(qdata) > 0:
+ print(qdata)
+
+ # input_tf_time = toUnixTimeSecond(input_tf)
+ # now_t = int(time.time())
+
+ # del_before_t = now_t - input_tf_time * 1000
+
+ sql = "delete from %s where addtime<'%d' " % (
+ table_name, qdata[0]['addtime'],)
+ writeLog("删除冗余数据:" + str(sql))
+ pdb.execute(sql)
+ return True
+
+
+def dataToDbTpl(tag='btc', input_tf="1m", start_time="2023-1-1", limit=300):
+ # tag = "btc"
+ # tf = "1m"
+ # start_time = "2023-1-1"
+ # end_time = "2023-3-2"
+ symbol = tag.lower() + '/USDT'
+ if not isHasTable(tag, input_tf):
+ createSql(tag, input_tf)
+ data = getDataFetch(symbol, start_time, input_tf, limit)
+ table_name = makeTableName(tag, input_tf)
+
+ dataToDb(table_name, data)
+ now = time.strftime("%m/%d %H:%M:%S")
+ data_len = len(data)
+ writeLog(now + "|数据获取成功!,总数:" + str(data_len))
+ else:
+ findAndUpdateData(tag, input_tf, start_time, limit)
+
+ return True
+
+
+def dataToDbList(input_tf="1m", start_time="2023-1-1"):
+
+ data = getConfigData()
+ if not 'token' in data:
+ writeLog("未设置同步配置,需要添加币种!")
+ return
+
+ for t in data['token']:
+ dataToDbTpl(t, input_tf, start_time)
+ time.sleep(4)
+
+
+def dataRunToDb():
+ data = getConfigData()
+ if not 'db' in data:
+ writeLog("数据库未设置!")
+ return
+
+ tag = "btc"
+ symbol = tag.upper() + '/USDT'
+
+ # pprint(exchange.fetch_ticker('XRP/USDT'))
+ limit_count = 1
+ start_time = "2023-1-1"
+ end_time = "2023-3-2"
+ tf = "1m"
+
+ if not isHasTable(tag, tf):
+ createSql(tag, tf)
+ data = getDataFetch(symbol, start_time, end_time, tf, limit_count)
+ table_name = makeTableName(tag, tf)
+
+ dataToDb(table_name, data)
+ china_datetime = str(datetime.now())
+ data_len = len(data)
+ writeLog(china_datetime + ":数据获取成功!,总数:" + str(data_len))
+ else:
+ findAndUpdateData(tag, tf, start_time, 30)
+ return data
+
+
+def startTask():
+ # 任务队列
+ try:
+ while True:
+ time.sleep(5)
+ except Exception as e:
+ time.sleep(60)
+ startTask()
+
+
+def setDaemon(t):
+ if sys.version_info.major == 3 and sys.version_info.minor >= 10:
+ t.daemon = True
+ else:
+ t.setDaemon(True)
+ return t
+
+
+def dataDay():
+ try:
+ while True:
+ if not isSetDbConf():
+ print("数据库未设置!")
+ else:
+ start_time = beforeDate(2 * 365)
+ dataToDbList("1d", start_time=start_time)
+ time.sleep(3600)
+ # time.sleep(10)
+ except Exception as e:
+ time.sleep(60)
+ dataDay()
+
+
+def data4h():
+ try:
+ while True:
+ if not isSetDbConf():
+ print("数据库未设置!")
+ else:
+ start_time = beforeDate(365)
+ dataToDbList("4h", start_time=start_time)
+ time.sleep(10)
+ except Exception as e:
+ time.sleep(60)
+ data4h()
+
+
+def data1h():
+ try:
+ while True:
+ if not isSetDbConf():
+ print("数据库未设置!")
+ else:
+ start_time = beforeDate(365)
+ dataToDbList("1h", start_time=start_time)
+ time.sleep(10)
+ except Exception as e:
+ print(e)
+ time.sleep(60)
+ data1h()
+
+
+def data15m():
+ try:
+ while True:
+ if not isSetDbConf():
+ print("数据库未设置!")
+ else:
+ start_time = beforeDate(365)
+ dataToDbList("15m", start_time=start_time)
+ time.sleep(10)
+ except Exception as e:
+ time.sleep(60)
+ data15m()
+
+
+def data5m():
+ try:
+ while True:
+ if not isSetDbConf():
+ print("数据库未设置!")
+ else:
+ start_time = beforeDate(180)
+ dataToDbList("5m", start_time=start_time)
+ time.sleep(10)
+ except Exception as e:
+ time.sleep(60)
+ data5m()
+
+
+def data1m():
+ try:
+ while True:
+ if not isSetDbConf():
+ print("数据库未设置!")
+ else:
+ start_time = beforeDate(30)
+ dataToDbList("1m", start_time=start_time)
+ time.sleep(10)
+ except Exception as e:
+ time.sleep(60)
+ data1m()
+
+
+def longRun():
+
+ # 日线
+ d1_tt = threading.Thread(target=dataDay)
+ d1_tt = setDaemon(d1_tt)
+ d1_tt.start()
+
+ # 4h
+ h4_tt = threading.Thread(target=data4h)
+ h4_tt = setDaemon(h4_tt)
+ h4_tt.start()
+
+ # 1h
+ h1_tt = threading.Thread(target=data1h)
+ h1_tt = setDaemon(h1_tt)
+ h1_tt.start()
+
+ # 15m
+ m15_tt = threading.Thread(target=data15m)
+ m15_tt = setDaemon(m15_tt)
+ m15_tt.start()
+
+ # 5m
+ m5_tt = threading.Thread(target=data5m)
+ m5_tt = setDaemon(m5_tt)
+ m5_tt.start()
+
+ # 1m
+ # h1m_tt = threading.Thread(target=data1m)
+ # h1m_tt = setDaemon(h1m_tt)
+ # h1m_tt.start()
+
+ startTask()
+
+
+def dataRun():
+ do_num = 0
+ while True:
+ if do_num > 1:
+ break
+ do_num = do_num + 1
+ dataRunToDb()
+ time.sleep(6)
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'run':
+ data5m()()
+ elif func == 'long':
+ longRun()
+ elif func == 'demo':
+ writeLog('111')
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/public_data/data.sh b/plugins/cryptocurrency_trade/ccxt/public_data/data.sh
new file mode 100644
index 000000000..974b1bb20
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/public_data/data.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+curPath=`pwd`
+
+# bash plugins/cryptocurrency_trade/ccxt/public_data/data.sh
+
+if [ -f ${curPath}/bin/activate ];then
+ source ${curPath}/bin/activate
+fi
+
+python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
diff --git a/plugins/cryptocurrency_trade/ccxt/public_data/demo.py b/plugins/cryptocurrency_trade/ccxt/public_data/demo.py
new file mode 100644
index 000000000..321929883
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/public_data/demo.py
@@ -0,0 +1,19 @@
+import time
+
+from datetime import datetime
+china_datetime = datetime.now()
+print(china_datetime)
+
+
+# date()
+tt = time.time() - 180 * 86400
+
+# print(date("%Y-%m-%d", time.time()))
+# t = datetime.strptime(str(time.time() - 180 * 86400), "%Y-%m-%d")
+# print(t)
+
+
+d = datetime.fromtimestamp(tt)
+# 精确到毫秒
+str1 = d.strftime("%Y-%m-%d")
+print(str1)
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/abs.py b/plugins/cryptocurrency_trade/ccxt/strategy/abs.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/common.py b/plugins/cryptocurrency_trade/ccxt/strategy/common.py
new file mode 100644
index 000000000..e787f1968
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/common.py
@@ -0,0 +1,352 @@
+import ccxt
+
+
+import time
+import sys
+import json
+import os
+import glob
+import threading
+
+import pandas as pd
+from decimal import Decimal
+from pprint import pprint
+from datetime import datetime
+
+# print(os.getcwd())
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = ccxt.poloniex()
+
+
+def calc_ClosingPriceWithOutStopLossPrice(open_price, stype='buy', profit=0.5):
+ # profit 百分比 %
+ v = 0
+ vf_len = len(str(open_price).split('.')[1])
+ if stype == 'buy':
+ v = open_price * (100 + profit) / 100
+ else:
+ v = open_price * (100 - profit) / 100
+ return round(v, vf_len)
+
+
+def roundVal(price, compare_price):
+ price = float(price)
+ csplite = str(compare_price).split('.')
+ clen = len(csplite)
+ if clen == 2:
+ vf_len = len(csplite[1])
+ return round(price, vf_len)
+ return price
+
+
+def roundValCeil(price, compare_price):
+ price = float(price)
+ csplite = str(compare_price).split('.')
+ clen = len(csplite)
+ if clen == 2:
+ vf_len = len(csplite[1]) - 1
+ return round(price, vf_len)
+ return price
+
+
+def multiply(a1, a2):
+ v = Decimal(str(a1)) * Decimal(str(a2))
+ return v
+
+
+def addition(a1, a2):
+ v = Decimal(str(a1)) + Decimal(str(a2))
+ return v
+
+
+def subtract(a1, a2):
+ v = Decimal(str(a1)) - Decimal(str(a2))
+ return v
+
+
+def divided(a1, a2):
+ v = Decimal(str(a1)) / Decimal(str(a2))
+ return v
+
+
+def calc_ClosingPrice(open_price, stop_loss_price, stype='buy', profit=0.5):
+ # profit 百分比 %
+ v = 0
+ vf_len = len(str(open_price).split('.')[1])
+ if stype == 'buy':
+ v = open_price * (100 + profit) / 100
+ diff = open_price - stop_loss_price
+ profit_price = open_price + diff * 1.5
+ if profit_price > v:
+ return round(profit_price, vf_len)
+ else:
+ v = open_price * (100 - profit) / 100
+ diff = stop_loss_price - open_price
+ profit_price = open_price - diff * 1.5
+ if profit_price < v:
+ return round(profit_price, vf_len)
+ return round(v, vf_len)
+
+
+def toDateFromInt(time_unix, tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"):
+ # 取格式时间
+ import time
+ os.environ['TZ'] = time_zone
+ time_str = time.localtime(time_unix)
+ time.tzset()
+
+ return time.strftime(tf_format, time_str)
+
+
+def getPluginName():
+ return 'cryptocurrency_trade'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def getUserCfgData():
+ data = getConfigData()
+ if 'user' in data:
+ try:
+ udata = mw.deDoubleCrypt('mw', data['user'])
+ udata = json.loads(udata)
+ return udata
+ except Exception as e:
+ pass
+ return []
+
+
+def initEx():
+ data = getUserCfgData()
+ # print(data)
+ if (len(data) > 0):
+ exchange = ccxt.okex({
+ "apiKey": data['app_key'],
+ "secret": data['secret'],
+ "password": data['password'],
+ })
+ return exchange
+ else:
+ print("初始化失败,检查原因!")
+ exchange = ccxt.poloniex()
+ return exchange
+
+
+def toUnixTimeSecond(tf="1m"):
+ if tf.find("m") > -1:
+ v = int(tf.replace("m", ''))
+ return v * 60
+
+ if tf.find("h") > -1:
+ v = int(tf.replace("h", ''))
+ return v * 3600
+
+ if tf.find("d") > -1:
+ v = int(tf.replace("d", ''))
+ return v * 86400
+ return 0
+
+
+def notifyMsg(msg, tf='15m', tag='btc'):
+ trigger_time = toUnixTimeSecond(tf)
+ return mw.notifyMessage(msg, '量化交易/' + tag, trigger_time)
+
+
+def makeTableName(input_type="btc", input_tf="1m"):
+ table_name = "ct_%s_%s" % (input_type, input_tf,)
+ return table_name
+
+
+def writeLog(log_str):
+ if __name__ == "__main__":
+ print(log_str)
+
+ log_file = getServerDir() + '/logs/strategy.log'
+ mw.writeFileLog(log_str, log_file)
+ return True
+
+
+def writeLogEx(log_str, tag='btc'):
+ tag = tag.replace('/', '_')
+ # 各种币的详细API日志
+ if __name__ == "__main__":
+ print(log_str)
+
+ log_file = getServerDir() + '/logs/strategy_' + tag + '.log'
+ mw.writeFileLog(log_str, log_file)
+ return True
+
+
+def writeLogErrorEx(log_str, tag='btc'):
+ tag = tag.replace('/', '_')
+ # 各种币的详细API日志
+ if __name__ == "__main__":
+ print(log_str)
+
+ log_file = getServerDir() + '/logs/strategy_' + tag + '.err.log'
+ mw.writeFileLog(log_str, log_file)
+ return True
+
+
+def pMysqlDb():
+ # pymysql
+ db = mw.getMyORM()
+ data = getConfigData()
+ db_data = data['db']
+
+ # print(db_data)
+ db.setHost(db_data['db_host'])
+ db.setPort(db_data['db_port'])
+ db.setUser(db_data['db_user'])
+ db.setPwd(db_data['db_pass'])
+ db.setDbName(db_data['db_name'])
+ return db
+
+
+def getOnlineData(symbol, input_tf="15m", limit=200):
+ bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+ df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
+ return df
+
+
+def toDataFrame(data):
+ # print(data)
+ data = sorted(data, key=lambda x: x["addtime"], reverse=False)
+
+ dfield = ['addtime', 'open', 'high', 'low', 'close']
+ v = {}
+ for dx in dfield:
+ v[dx] = []
+
+ for x in data:
+ for i in range(len(x)):
+ field = dfield[i]
+ # print(i, field)
+ v[field].append(x[field])
+ # pprint(data)
+ frame = pd.DataFrame(v)
+ # frame = frame.sort_values(by=['addtime'])
+
+ frame['dt'] = pd.to_datetime(frame['addtime'], unit='s')
+ frame.set_index('dt', inplace=True)
+ frame.index = frame.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
+ return frame
+
+
+def getDataFromDb(tf="1m", tag='btc', limit=1000):
+ tn = makeTableName(tag, tf)
+ sql = 'select addtime,open,high,low,close from ' + \
+ tn + ' order by addtime desc limit ' + str(limit)
+
+ pdb = pMysqlDb()
+ fdata = pdb.query(sql)
+ # print(fdata)
+ return fdata
+
+
+def getDataFromDb_DF(tf="5m", tag='btc', limit=1000):
+ data = getDataFromDb(tf, tag, limit)
+ rdata = toDataFrame(data)
+ return rdata
+
+
+# 消息模板类
+class MsgTpl():
+
+ __name = ''
+ __strategy_name = ''
+ __time_frame = ''
+ __content = ''
+ __open_time = ''
+ __strategy_dt = ''
+
+ __stop_loss_price = ''
+ __closing_price = ''
+ __open_price = ''
+
+ __msg = ''
+
+ def setName(self, name):
+ self.__name = name
+
+ def setStrategyName(self, name):
+ self.__strategy_name = name
+
+ def setTimeFrame(self, tf):
+ self.__time_frame = tf
+
+ def setContent(self, content):
+ self.__content = content
+
+ def setOpenTime(self, time):
+ self.__open_time = toDateFromInt(time)
+
+ def setStrategicDt(self, stype='buy'):
+ if stype == 'buy':
+ self.__strategy_dt = '做多'
+ else:
+ self.__strategy_dt = '做空'
+
+ def setStopLossPrice(self, price):
+ self.__stop_loss_price = price
+
+ def setClosingPrice(self, price):
+ self.__closing_price = price
+
+ def setOpenPrice(self, price):
+ self.__open_price = price
+
+ def setMsg(self, msg):
+ self.__msg = msg
+
+ def toText(self):
+ msg = ''
+ msg += '名称:' + self.__name + "\n"
+
+ if self.__strategy_name != '':
+ msg += '策略名称:' + self.__strategy_name + "\n"
+
+ if self.__time_frame != '':
+ msg += '时间周期:' + self.__time_frame + "\n"
+
+ if self.__content != '':
+ msg += '策略描述:' + self.__content + "\n"
+
+ if self.__open_time != '':
+ msg += '开盘时间:' + self.__open_time + "\n"
+
+ if self.__strategy_dt != '':
+ msg += '开仓方向:' + self.__strategy_dt + "\n"
+
+ if self.__open_price != '':
+ msg += '开仓价:' + str(self.__open_price) + "\n"
+
+ if self.__stop_loss_price != '':
+ msg += '止损价:' + str(self.__stop_loss_price) + "\n"
+
+ if self.__closing_price != '':
+ msg += '止盈价:' + str(self.__closing_price) + "\n"
+
+ msg += '发送时间:' + mw.getDateFromNow() + "\n"
+
+ if self.__msg != '':
+ msg += __msg
+ return msg
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/func_test.py b/plugins/cryptocurrency_trade/ccxt/strategy/func_test.py
new file mode 100644
index 000000000..8b79839e0
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/func_test.py
@@ -0,0 +1,57 @@
+# import ccxt
+# import talib
+
+import sys
+import os
+import time
+# import pandas as pd
+from pprint import pprint
+from decimal import Decimal
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/func_test.py run
+
+# common.notifyMsg("任务开始")
+
+
+def toUnixTimeSecond(tf="1m"):
+ if tf.find("m") > -1:
+ v = int(tf.replace("m", ''))
+ return v * 60
+
+ if tf.find("h") > -1:
+ v = int(tf.replace("h", ''))
+ return v * 3600
+
+ if tf.find("d") > -1:
+ v = int(tf.replace("d", ''))
+ return v * 86400
+ return 0
+
+
+def multiply(a1, a2):
+ v = Decimal(str(a1)) * Decimal(str(a2))
+ return v
+
+# print(toUnixTimeSecond("1d"))
+
+# f = 19911.2
+# s = common.calc_ClosingPrice(f, 19890.2, 'buy')
+# print(s)
+
+# print(sys.version_info)
+# os.environ['TZ'] = 'Europe/London'
+# time.tzset()
+# t = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
+# print(t)
+
+# v = multiply(26236.8, 0.003)
+# print(float(v))
+# print(v)
+
+print(float('0.00255234') + float('-0.00000255234'))
+v1 = common.addition('0.00255234', '-0.00000255234')
+print(v1)
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py b/plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py
new file mode 100644
index 000000000..f15b29875
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py
@@ -0,0 +1,440 @@
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py run
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py long
+
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import json
+import pandas as pd
+# import pandas_ta as ta
+from pprint import pprint
+import numpy as np
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = ccxt.gate({
+ "apiKey": '756e1ff80526cb0ac9620c75680fc506',
+ "secret": '95ac89362056bcf12ce37aabff6eb7ec78185483105f39b4a35e8dc8db8b4d3c',
+})
+
+exchange.load_markets()
+
+# 默认开仓数据
+# default_open_num = 10
+
+default_open = {
+ "BTC/USDT": 0.01,
+ 'ETH/USDT': 30,
+ 'DOT/USDT': 30,
+ 'CEL/USDT': 30,
+}
+
+default_sell = {
+ "BTC/USDT": 0.01,
+ 'ETH/USDT': 0.01,
+ 'DOT/USDT': 5,
+ 'CEL/USDT': 85,
+}
+
+
+# 做多开仓
+def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ common.writeLogEx('------做多----------------------------------', symbol)
+
+ default_open_num = default_open[symbol]
+ # 做多开仓 | 市价
+ data = exchange.createMarketBuyOrder(
+ symbol, default_open_num, {"tdMode": "cross"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ # 做多-止损价大于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 - 0.003))
+
+ # property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
+ property_val = common.addition(
+ order_data['info']['accFillSz'], order_data['info']['fee'])
+ property_val = float(property_val)
+
+ # 可平仓的数量
+ property_val = common.roundValCeil(
+ property_val, order_data['info']['accFillSz'])
+
+ common.writeLogEx('可平仓资产:' + str(property_val), symbol)
+
+ # 止盈价
+ diff = float(open_price) - float(stop_loss_price)
+ closing_price_c = float(open_price) + (diff * 2)
+ closing_price = float(open_price) * float((1 + profit))
+ # 选择盈利多的
+ if closing_price_c > closing_price:
+ closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, stop_loss_price)
+ # stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价/止盈价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ # 止损条件单
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ # 止赢条件单
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
+
+ common.writeLogEx('止赢数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做多 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做多开仓
+ # profit 百分比
+ try:
+ return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+ common.writeLogEx('------做空----------------------------------', symbol)
+
+ # 计算借币卖币多多少,以USDT为基准
+ # sell_num = float(default_open_num) / float(stop_loss_price)
+ # sell_num = round(sell_num, 8)
+
+ sell_num = default_sell[symbol]
+ # 做空开仓 | 市价
+ data = exchange.createMarketSellOrder(
+ symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
+
+ # 做空-止损价小于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 + 0.003))
+
+ # 止盈价
+ diff = float(stop_loss_price) - float(open_price)
+ closing_price_c = float(open_price) - (diff * 2)
+ closing_price = float(open_price) * float((1 - profit))
+ # 选择盈利多的
+ if closing_price_c < closing_price:
+ closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, open_price)
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ sl_amount = common.multiply(stop_loss_price, sell_num)
+ # 解决平仓时,未全部平仓
+ sl_amount = common.addition(sl_amount, 0.1)
+ common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ sl_amount), stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', sl_amount, stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ # 设置 -止盈价
+ tp_amount = common.multiply(closing_price, sell_num)
+ # 解决平仓时,未全部平仓
+ tp_amount = common.addition(tp_amount, 0.1)
+ common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ tp_amount), closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', tp_amount, closing_price, tp_exchange_params)
+
+ common.writeLogEx('止盈价数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做空 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做空开仓
+ # profit 百分比
+ try:
+ return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def getOnlineData(symbol, input_tf="15m", limit=230):
+ bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+ df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
+ df.set_index('dt', inplace=True)
+ df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
+ return df
+
+
+def isKdj(last, last_pre):
+ # 判断是否是金叉
+ if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
+ return True
+ return False
+
+
+def isDeadFork(last, last_pre):
+ # 判断是否是死叉
+ if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
+ return True
+ return False
+
+
+def getMACD(df, lenght=-60):
+ if len(df['close'].values) < 100:
+ return False, None
+
+ close_p = df['close'].values
+ df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
+ fastperiod=12,
+ slowperiod=26,
+ signalperiod=9)
+ return df[lenght:]
+
+
+def getEMA(df):
+ close_p = df['close'].values
+ df['ema'] = talib.EMA(np.array(close_p), timeperiod=200)
+ return df
+
+
+def doneMacd(data, tag, timeframe):
+ data = getEMA(data)
+
+ ma_data = getMACD(data)
+ # alen = len(data)
+ # print(ma_data)
+
+ t_data = ma_data.tail(3)
+ print(t_data)
+
+ last_pre = t_data.iloc[0]
+ last = t_data.iloc[1]
+
+ print(last)
+ # print(last.index)
+
+ # print('close:', last['close'], 'ema:', last['ema'])
+
+ obj = common.MsgTpl()
+ symbol = tag.upper() + '/USDT'
+ obj.setName(symbol)
+ obj.setStrategyName("MACD检查")
+ obj.setTimeFrame(timeframe)
+ obj.setOpenTime(last['timestamp'] / 1000)
+
+ now_data = t_data.iloc[2]
+ # print('now_data:', now_data)
+
+ # msg = obj.toText()
+ # print(msg)
+
+ if isKdj(last, last_pre) and last['close'] > last['ema']:
+ # if isKdj(last, last_pre):
+
+ # closing_price = common.calc_ClosingPrice(
+ # now_data['close'], last_pre['low'], 'buy')
+ # # print('closing_price:', closing_price)
+
+ # 做多止损点
+ stop_loss_price = last_pre['low']
+ buy_status, open_price, closing_price = onBuyOrder(
+ symbol, stop_loss_price, 0.005, timeframe)
+
+ if buy_status:
+ obj.setStrategicDt('buy')
+ # 做多止损点
+ obj.setStopLossPrice(str(stop_loss_price))
+ obj.setOpenPrice(str(open_price))
+ obj.setClosingPrice(str(closing_price))
+ obj.setContent("检查到金叉状态")
+ msg = obj.toText()
+ print(msg)
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+ if isDeadFork(last, last_pre) and last['close'] < last['ema']:
+ # if isDeadFork(last, last_pre):
+ # closing_price = common.calc_ClosingPrice(
+ # now_data['close'], last_pre['high'], 'sell')
+
+ # 做空止损点
+ stop_loss_price = last_pre['high']
+ sell_status, open_price, closing_price = onSellOrder(
+ symbol, stop_loss_price, 0.005, timeframe)
+ if sell_status:
+ obj.setStopLossPrice(str(stop_loss_price))
+ obj.setOpenPrice(str(open_price))
+ obj.setClosingPrice(str(closing_price))
+ obj.setStrategicDt('sell')
+ obj.setContent("检查到死叉状态")
+ msg = obj.toText()
+ print(msg)
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+
+def mainProcess(tag, timeframe='15m'):
+ symbol = tag.upper() + '/USDT'
+ data = getOnlineData(symbol, timeframe)
+
+ doneMacd(data, tag, timeframe)
+
+
+def foreachList():
+ tag_list = ['btc']
+
+ for tag in tag_list:
+ mainProcess(tag, '15m')
+ time.sleep(1)
+
+
+def longRun():
+ while True:
+ foreachList()
+ time.sleep(3)
+
+
+def debug():
+ while True:
+ mainProcess('xrp', '1m')
+ time.sleep(3)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'long':
+ longRun()
+ elif func == 'run':
+ debug()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/hammer_robot.py b/plugins/cryptocurrency_trade/ccxt/strategy/hammer_robot.py
new file mode 100644
index 000000000..c03b643cf
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/hammer_robot.py
@@ -0,0 +1,138 @@
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import pandas as pd
+import pandas_ta as ta
+from pprint import pprint
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/hammer_robot.py run
+
+
+pd.set_option('display.max_rows', None)
+
+exchange = common.initEx()
+
+exchange.load_markets()
+
+entry_rsi = 30
+exit_rsi = 40
+
+
+symbol = 'XRP/USDT'
+timeframe = '15m'
+
+tf_mult = exchange.parse_timeframe(timeframe) * 1000
+
+
+def indicators(data):
+
+ data['rsi'] = data.ta.rsi(length=10)
+ data['ema'] = data.ta.ema(length=200)
+
+ # close_p = data['close'].values
+ # data['rsi'] = talib.RSI(close_p, timeperiod=10)
+ # data['ema'] = talib.EMA(close_p, timeperiod=200)
+ return data
+
+
+def check_buy_sell_signals(df):
+ last_row_index = len(df.index) - 1
+ lastest_rsi = round(df['rsi'].iloc[-1], 2)
+ lastest_price = round(df['close'].iloc[-1], 5)
+ lastest_ema = round(df['ema'].iloc[-1], 5)
+ lastest_ts = df['timestamp'].iloc[-1]
+
+ msg = "lastest_rsi:" + str(lastest_rsi) + " < entry_rsi:" + str(entry_rsi)
+ msg += ",lastest_price:" + \
+ str(lastest_price) + " > lastest_ema:" + str(lastest_ema)
+ print(msg)
+
+ long_cond = (lastest_rsi < entry_rsi) and (lastest_price > lastest_ema)
+ if long_cond:
+ print("买入")
+ order = exchange.create_market_buy_order(symbol, 1)
+
+ closed_orders = exchange.fetchClosedOrders(symbol, limit=2)
+ if len(closed_orders) > 0:
+ print("closed_orders:", closed_orders)
+ most_recent_closed_order = closed_orders[-1]
+ diff = lastest_ts - most_recent_closed_order['timestamp']
+ last_buy_signal_cnt = int(diff / tf_mult)
+
+ exit_cond = (lastest_rsi > exit_rsi) and (last_buy_signal_cnt > 10)
+ if exit_cond:
+ print("卖出")
+ order = exchange.create_market_sell_order(symbol, 1)
+ return
+
+
+def get_hammer(df, lenght):
+ # 影线要大于body的多少倍
+ factor = 2
+ hl_range = df['high'] - df['low']
+
+ body_hi = df.apply(lambda x: max(x['close'], x['open']), axis=1)
+ body_lo = df.apply(lambda x: min(x['close'], x['open']), axis=1)
+ body = body_hi - body_lo
+
+ body_avg = ta.ema(body, lenght=lenght)
+ small_body = body < body_avg
+
+ # 上下影线站body的百分比
+ shadow_percent = 10
+
+ # 上影线
+ up_shadow = df['high'] - body_hi
+ dn_shadow = body_lo - df['low']
+ has_up_shadow = up_shadow > shadow_percent / 100 * body
+ has_dn_shadow = dn_shadow > shadow_percent / 100 * body
+
+ downtrend = df['close'] < ta.ema(df['close'], 50)
+ bullish_hammer = downtrend & small_body & (body > 0) & (
+ dn_shadow >= factor * body) & (has_up_shadow == False)
+ return bullish_hammer
+
+
+def runBot():
+ bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=200)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+
+ # format='%Y-%m-%d %H:%M:%S',
+ df['dt'] = pd.to_datetime(
+ df['timestamp'], unit="ms")
+
+ df['hammer'] = get_hammer(df, 10)
+
+ lastest_hammer = df.iloc[-1, -1]
+ lastest_price = df.iloc[-1, 0]
+
+ print("lastest_price:" + str(lastest_price))
+ print("lastest_hammer:" + str(lastest_hammer))
+ print(df.tail())
+
+ if lastest_hammer:
+ print("购买,做多")
+ notifyMsg("购买,做多")
+
+
+def longRunBot():
+ common.notifyMsg("任务开始")
+ while True:
+ runBot()
+ time.sleep(10)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'run':
+ longRunBot()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/info.json b/plugins/cryptocurrency_trade/ccxt/strategy/info.json
new file mode 100644
index 000000000..2e5071221
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/info.json
@@ -0,0 +1,33 @@
+[
+ {
+ "id":"01",
+ "name": "MACD-KDJ策略检测|1h",
+ "file": "macd_kdj.py"
+ },
+ {
+ "id":"02",
+ "name": "通知测试|60s",
+ "file": "notify_demo.py"
+ },
+ {
+ "id":"03",
+ "name": "okex|macd交易策略|15m",
+ "file": "online_macd_trade.py"
+ },
+ {
+ "id":"04",
+ "name": "okex|动量交易策略|5m",
+ "file": "momentun_trade.py"
+ },
+ {
+ "id":"05",
+ "name": "gate.io|动量交易策略|5m",
+ "file": "gate_100.py"
+ },
+ {
+ "id":"06",
+ "name": "仅提醒|Vega交易策略|5m",
+ "file": "vega_trade.py"
+ }
+
+]
\ No newline at end of file
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py b/plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py
new file mode 100644
index 000000000..de8f023b8
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py
@@ -0,0 +1,121 @@
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py run
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py long
+
+
+import ccxt
+import talib
+import pandas as pd
+import time
+
+import sys
+import os
+from pprint import pprint
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+
+pd.set_option('display.max_rows', None)
+
+# import warnings
+# warnings.filterwarnings('error')
+
+
+def getMACD(df, lenght=-60):
+ if len(df['close'].values) < 100:
+ return False, None
+
+ close_p = df['close'].values
+ df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
+ fastperiod=12,
+ slowperiod=26,
+ signalperiod=9)
+ return True, df[lenght:]
+
+
+def isKdj(last, last_pre):
+ # 判断是否是金叉
+ if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
+ return True
+ return False
+
+
+def isDeadFork(last, last_pre):
+ # 判断是否是死叉
+ if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
+ return True
+ return False
+
+
+def checkData():
+ tag = 'btc'
+ tf_frame = '1h'
+
+ data = common.getDataFromDb_DF(tf_frame, tag, 300)
+
+ # print(data)
+ b, r = getMACD(data)
+ if b:
+ # rlen = len(r)
+ # r = r.copy()
+ # r['dt'] = pd.to_datetime(
+ # r['addtime'], unit='s')
+
+ # r['dt'] = r['dt'].dt.tz_convert('Asia/Shanghai')
+ # print(r)
+ rlen = len(r)
+
+ t_data = r.tail(2)
+ # print(r.tail(2))
+
+ last_pre = t_data.iloc[0]
+ last = t_data.iloc[1]
+
+ # print(last_pre)
+ # print(last)
+ # print(last_pre['addtime'])
+ # print(last_pre['open'])
+ # print(last_pre['high'])
+ # print(last_pre['low'])
+ # print(last_pre['close'])
+
+ # print(last['addtime'])
+ # print(last['open'])
+ # print(last['high'])
+ # print(last['low'])
+ # print(last['close'])
+
+ now = mw.getDateFromNow()
+ if isKdj(last, last_pre):
+ msg = now + "|{}|{}|检查到金叉状态!".format(tag, tf_frame)
+ common.notifyMsg(msg, tf_frame, tag)
+ common.writeLog(msg)
+
+ if isDeadFork(last, last_pre):
+ msg = now + "|{}|{}|检查到死叉状态!".format(tag, tf_frame)
+ common.notifyMsg(msg, tf_frame, tag)
+ common.writeLog(msg)
+
+
+def run():
+ checkData()
+
+
+def longRun():
+ while True:
+ checkData()
+ time.sleep(3)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'run':
+ run()
+ elif func == 'long':
+ longRun()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py b/plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py
new file mode 100644
index 000000000..fe798278e
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py
@@ -0,0 +1,505 @@
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py run
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py long
+
+
+# 动量策略交易
+
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import json
+import pandas as pd
+# import pandas_ta as ta
+from pprint import pprint
+import numpy as np
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = common.initEx()
+exchange.load_markets()
+
+# 默认开仓数据
+# default_open_num = 70
+# default_sell_num = 0.003
+
+default_open = {
+ 'BTC/USDT': 30,
+ 'XRP/USDT': 30,
+}
+
+default_sell = {
+ 'BTC/USDT': 0.001,
+ 'XRP/USDT': 100,
+}
+
+
+# 做多开仓
+def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ common.writeLogEx('------做多----------------------------------', symbol)
+
+ default_open_num = default_open[symbol]
+ # 做多开仓 | 市价
+ data = exchange.createMarketBuyOrder(
+ symbol, default_open_num, {"tdMode": "cross"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ # 修正小数点位数
+ open_price = common.roundVal(open_price, stop_loss_price)
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+
+ # 做多-止损价大于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 - 0.003))
+
+ # property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
+
+ property_val = common.addition(order_data['info'][
+ 'accFillSz'], order_data['info']['fee'])
+ property_val = float(property_val)
+ # 可平仓的数量
+ # property_val = common.roundValCeil(
+ # property_val, order_data['info']['accFillSz'])
+
+ common.writeLogEx('可平仓资产:' + str(property_val), symbol)
+
+ # 止盈价
+ diff = float(open_price) - float(stop_loss_price)
+ closing_price = float(open_price) + (diff * 1.5)
+ # closing_price = float(open_price) * float((1 + profit))
+ # # 选择盈利多的
+ # if closing_price_c > closing_price:
+ # closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, open_price)
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价/止盈价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ # 止损条件单
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ # 止赢条件单
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
+
+ common.writeLogEx('止盈数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做多 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做多开仓
+ # profit 百分比
+ try:
+ return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+ common.writeLogEx('------做空----------------------------------', symbol)
+
+ # 计算借币卖币多多少,以USDT为基准
+ # sell_num = float(default_open_num) / float(stop_loss_price)
+ # sell_num = round(sell_num, 8)
+ # sell_num = default_sell_num
+ sell_num = default_sell[symbol]
+
+ # 做空开仓 | 市价
+ data = exchange.createMarketSellOrder(
+ symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ # 修正
+ open_price = common.roundVal(open_price, stop_loss_price)
+
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+ common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
+
+ # 做空-止损价小于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 + 0.003))
+
+ # 止盈价
+ diff = float(stop_loss_price) - float(open_price)
+ closing_price = float(open_price) - (diff * 1.5)
+ # closing_price = float(open_price) * float((1 - profit))
+ # 选择盈利多的
+ # if closing_price_c < closing_price:
+ # closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, open_price)
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ sl_amount = common.multiply(stop_loss_price, sell_num)
+ # 解决平仓时,未全部平仓
+ sl_amount = common.addition(sl_amount, 0.1)
+ common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ sl_amount), stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', float(sl_amount), stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ # 设置 -止盈价
+ # tp_amount = closing_price * sell_num
+ tp_amount = common.multiply(closing_price, sell_num)
+ # 解决平仓时,未全部平仓
+ tp_amount = common.addition(tp_amount, 0.1)
+ common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ tp_amount), closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', float(tp_amount), closing_price, tp_exchange_params)
+
+ common.writeLogEx('止盈价数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做空 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做空开仓
+ # profit 百分比
+ try:
+ return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def getOnlineData(symbol, input_tf="15m", limit=230):
+ bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+ df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
+ df.set_index('dt', inplace=True)
+ df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
+ return df
+
+
+def isKdj(last, last_pre):
+ # 判断是否是金叉
+ if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
+ return True
+ return False
+
+
+def isDeadFork(last, last_pre):
+ # 判断是否是死叉
+ if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
+ return True
+ return False
+
+
+def getMACD(df, lenght=-60):
+ if len(df['close'].values) < 100:
+ return False, None
+
+ close_p = df['close'].values
+ df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
+ fastperiod=12,
+ slowperiod=26,
+ signalperiod=9)
+ return df[lenght:]
+
+
+def getTarget(df):
+ close = df['close'].values
+ df['ema'] = talib.EMA(np.array(close), timeperiod=10)
+ df['ema_200'] = talib.EMA(np.array(close), timeperiod=200)
+ df['ma'] = talib.MA(close, timeperiod=10)
+ df['rsi'] = talib.RSI(close, timeperiod=14)
+ return df
+
+
+# 多头信号
+def isBuyCrondSignal(last):
+ if last['ema'] > last['ma'] and (last['rsi'] > 50 and last['rsi'] < 70) and last['low'] >= last['ma'] and last['close'] > last['open']:
+ return True
+ return False
+
+
+def isBuyFristSignal(data):
+ data = data.sort_values(by=['timestamp'], ascending=False)
+ # print(data)
+ data_len = len(data)
+ signal_num = 0
+
+ first_data = data.iloc[1]
+ # print(1, first_data['close'], first_data['ema'])
+ is_buy_signal = isBuyCrondSignal(first_data)
+
+ for x in range(2, data_len):
+ tmp = data.iloc[x]
+ # print(data.iloc[x])
+ # print(x, tmp['close'], tmp['ema'])
+ if isBuyCrondSignal(tmp):
+ signal_num = + 1
+
+ # print('signal_num:', signal_num)
+
+ if (tmp['ema'] < tmp['ma']):
+ break
+
+ if str(tmp['ema']) == 'nan':
+ break
+
+ print("is_buy_signal:", is_buy_signal, 'signal_num:', signal_num)
+ if is_buy_signal and signal_num == 0:
+ return True
+
+ return False
+
+
+# 空头信号
+def isSellCrondSignal(last):
+ if last['ema'] < last['ma'] and (last['rsi'] > 30 and last['rsi'] < 50) and last['high'] <= last['ema'] and last['close'] < last['open']:
+ return True
+ return False
+
+
+def isSellFristSignal(data):
+ data = data.sort_values(by=['timestamp'], ascending=False)
+ # print(data)
+ data_len = len(data)
+ signal_num = 0
+
+ first_data = data.iloc[1]
+ # print(1, first_data['close'], first_data['ema'])
+ is_sell_signal = isSellCrondSignal(first_data)
+
+ for x in range(2, data_len):
+ tmp = data.iloc[x]
+ # print(data.iloc[x])
+ # print(x, tmp['close'], tmp['ema'])
+ if isSellCrondSignal(tmp):
+ signal_num = + 1
+
+ # print('signal_num:', signal_num)
+
+ if (tmp['ema'] < tmp['ma']):
+ break
+
+ if str(tmp['ema']) == 'nan':
+ break
+
+ print("is_sell_signal:", is_sell_signal, 'signal_num:', signal_num)
+ if is_sell_signal and signal_num == 0:
+ return True
+
+ return False
+
+
+def monentunTrade(data, tag, timeframe):
+ data = getTarget(data)
+ # print(data)
+
+ key_data = data.tail(3)
+ print(key_data)
+ last_pre = key_data.iloc[0]
+ last = key_data.iloc[1]
+
+ obj = common.MsgTpl()
+ symbol = tag.upper() + '/USDT'
+ obj.setName(symbol)
+ obj.setStrategyName("动量交易策略")
+ obj.setTimeFrame(timeframe)
+ obj.setOpenTime(last['timestamp'] / 1000)
+
+ # 买入信号,并且收盘价要大于200 ema
+ if isBuyFristSignal(data) and last['close'] > last['ema_200']:
+ obj.setStrategicDt('buy')
+
+ # 做多止损点
+ stop_loss_price = last['ma']
+ stop_loss_price = common.roundVal(stop_loss_price, last['open'])
+ buy_status, open_price, closing_price = onBuyOrder(
+ symbol, stop_loss_price, 0.005, timeframe)
+
+ if buy_status:
+ # 做多止损点
+ obj.setStopLossPrice(str(stop_loss_price))
+ obj.setOpenPrice(str(open_price))
+ obj.setClosingPrice(str(closing_price))
+ obj.setContent("动量交易策略做多!")
+ msg = obj.toText()
+ print(msg)
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+ # 卖出信号,并且收盘价要小于200 ema
+ # if isSellFristSignal(data) and last['close'] < last['ema_200']:
+ # obj.setStrategicDt('sell')
+
+ # stop_loss_price = last['ma']
+ # stop_loss_price = common.roundVal(stop_loss_price, last['open'])
+ # sell_status, open_price, closing_price = onSellOrder(
+ # symbol, stop_loss_price, 0.005, timeframe)
+ # if sell_status:
+ # obj.setStopLossPrice(str(stop_loss_price))
+ # obj.setOpenPrice(str(open_price))
+ # obj.setClosingPrice(str(closing_price))
+ # obj.setStrategicDt('sell')
+ # obj.setContent("动量交易策略作空!")
+ # msg = obj.toText()
+ # print(msg)
+ # common.notifyMsg(msg, timeframe, tag)
+ # common.writeLog(msg)
+
+
+def mainProcess(tag, timeframe='15m'):
+ symbol = tag.upper() + '/USDT'
+ data = getOnlineData(symbol, timeframe)
+
+ monentunTrade(data, tag, timeframe)
+
+
+def foreachList():
+ tag_list = ['btc', 'xrp']
+ for tag in tag_list:
+ mainProcess(tag, '15m')
+ time.sleep(1)
+
+
+def longRun():
+ while True:
+ foreachList()
+ time.sleep(1)
+
+
+def debug():
+ while True:
+ mainProcess('btc', '5m')
+ time.sleep(1)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'long':
+ longRun()
+ elif func == 'run':
+ debug()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py b/plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py
new file mode 100644
index 000000000..72474e729
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py
@@ -0,0 +1,48 @@
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py run
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py long
+
+
+import ccxt
+import talib
+import pandas as pd
+import time
+
+import sys
+import os
+from pprint import pprint
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+
+def run():
+ print('debug')
+
+
+def longRun():
+ while True:
+
+ obj = common.MsgTpl()
+ obj.setName("通知测试")
+ obj.setStrategyName("无")
+ obj.setTimeFrame("1m")
+ obj.setContent("60s通知测试")
+ msg = obj.toText()
+ print(msg)
+ common.notifyMsg(msg, '1m', 'debug')
+ common.writeLog(msg)
+ time.sleep(3)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'run':
+ run()
+ if func == 'long':
+ longRun()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py b/plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py
new file mode 100644
index 000000000..50b6d4ac7
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py
@@ -0,0 +1,294 @@
+# cd /www/server/mdserver-web && source bin/activate
+
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_buy_open
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_buy_close
+
+
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_sell_open
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_sell_cloe
+
+# 获取仓位数据
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_get_trade
+
+# API地址
+# https://www.gate.io/docs/developers/apiv4/zh_CN/#api
+
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import pandas as pd
+# import pandas_ta as ta
+from pprint import pprint
+import numpy as np
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = ccxt.gate({
+ "apiKey": '756e1ff80526cb0ac9620c75680fc506',
+ "secret": '95ac89362056bcf12ce37aabff6eb7ec78185483105f39b4a35e8dc8db8b4d3c',
+})
+
+exchange.load_markets()
+
+
+def t_get_trade():
+ data = exchange.fetchPositions()
+ print(data)
+
+
+def btc_test():
+ closing_price = 24599.9
+ stop_loss_price = 24740.4
+
+ # ---------------------------
+ stop_loss_args = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+ print('---------止损价 执行 START ----------------------------------')
+
+ sl_amount = stop_loss_price * 0.001
+ print(amount)
+ data = exchange.create_order(
+ 'BTC/USDT', 'limit', 'buy', sl_amount, stop_loss_price, stop_loss_args)
+ print(data)
+ print('---------止损价 执行 END ----------------------------------')
+
+ print('---------止盈价 执行 START ----------------------------------')
+
+ closing_price_args = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ cp_amount = closing_price * 0.001
+ print(amount)
+ data = exchange.create_order(
+ 'BTC/USDT', 'limit', 'buy', cp_amount, closing_price, closing_price_args)
+ print(data)
+ print('---------止盈价 执行 END ----------------------------------')
+
+
+def testK_buy_open_sz():
+ # 做多开仓
+
+ data = exchange.fetchTicker('BTC/USDT')
+ print(data)
+ # data = exchange.createMarketBuyOrder('DOT/USDT', 1, {"tdMode": "cross"})
+ # print(data)
+ # print(type(data))
+
+
+def testK_buy_open():
+ # 做多开仓
+
+ # print(dir(exchange))
+
+ # exchange['options']['createMarketBuyOrderRequiresPrice'] = False
+ data = exchange.fetch_ticker('BTC/USDT')
+ print("now price:", data['ask'])
+
+ amount = 0.001
+ price = data['ask']
+ cost = amount * float(price)
+ print('total price:', cost)
+ print('amount price:', amount)
+ # a_amount = round(amount / data['ask'], 5)
+ # print('amount :', amount)
+ # print('amount :', str(amount))
+
+ #
+ data = exchange.createOrder(
+ "BTC/USDT", type="limit", side="buy", amount=amount, price=price, params={'account': "cross_margin"})
+ # data = exchange.createMarketBuyOrder('BTC/USDT', amount)
+ print(data)
+ print(type(data))
+
+ # 数据
+ # {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
+
+ # 查询委托单
+ # order_id = data['info']['ordId']
+ # order_id = '554437054223302656'
+ # data = exchange.fetchOrder(order_id, 'BTC/USDT')
+ # print(data['info'])
+ # {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
+
+ # open_price = data['info']['avgPx']
+ # print("开仓平均价", open_price)
+
+ # # 止盈价
+ # closing_price = float(open_price) * float((1 + 0.005))
+ # closing_price = common.roundVal(closing_price, open_price)
+ # print("止盈价", closing_price)
+
+ # # 止损价
+ # stop_loss_price = float(open_price) * float((1 - 0.01))
+ # stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ # print("止损价", stop_loss_price)
+
+ # property_val = float(data['info']['accFillSz']) + \
+ # float(data['info']['fee'])
+
+ # # 相同位数
+ # property_val = common.roundValCeil(property_val, data['info']['accFillSz'])
+ # print("可平仓资产", property_val)
+
+ # closed_orders = exchange.fetchClosedOrders('DOT/USDT', limit=2)
+ # print('closed_orders', closed_orders)
+
+ # print(exchange.fetchBalance())
+
+ # 可以用,限价单
+ # time.sleep(1)
+ # data = exchange.createLimitSellOrder(
+ # 'DOT/USDT', property_val, closing_price, {"tdMode": "cross", 'ccy': 'USDT', "reduceOnly": True})
+ # print(data)
+ # print(type(data))
+
+ # 止损价
+ # 止盈价
+
+ # exchange_params = {
+ # 'ccy': "USDT",
+ # 'reduceOnly': True,
+ # 'tdMode': "cross",
+ # 'tpOrdPx': "-1",
+ # 'tpTriggerPx': closing_price,
+ # 'slOrdPx': "-1",
+ # 'slTriggerPx': stop_loss_price,
+ # }
+ # print('---------止损价 执行----------------------------------')
+
+ # data = exchange.create_order(
+ # 'BTC/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
+ # print(data)
+ # print(type(data))
+
+ # print('---------止盈价 执行----------------------------------')
+
+ # exchange_params = {
+ # 'stopPrice': closing_price,
+ # 'type': 'stopLimit',
+ # }
+ # data = exchange.create_order(
+ # 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
+ # print(data)
+ # print(type(data))
+
+
+def testK_buy_close():
+ # 平多
+ data = exchange.create_limit_buy_order(
+ 'DOT/USDT', 1, 5, {"tdMode": "cross"})
+ print(data)
+ print(type(data))
+
+ # closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
+ # print(closed_orders)
+
+ # closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
+ # print(closed_orders)
+
+
+def testK_sell_open():
+ # 做空开仓
+
+ # data = exchange.createMarketSellOrder(
+ # 'DOT/USDT', 1, {"tdMode": "cross", 'ccy': "USDT", })
+ # print(data)
+ # print(type(data))
+
+ # 数据
+ # {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
+
+ # # 查询委托单
+ # order_id = data['info']['ordId']
+ order_id = '554516809819832320'
+ data = exchange.fetchOrder(order_id, 'DOT/USDT')
+ print(data['info'])
+ # {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
+
+ open_price = data['info']['avgPx']
+
+ print("开仓平均价", open_price)
+
+ # 止盈价
+ closing_price = float(open_price) * float((1 - 0.005))
+ closing_price = common.roundVal(closing_price, open_price)
+ print("止盈价", closing_price)
+
+ # 止损价
+ stop_loss_price = float(open_price) * float((1 + 0.01))
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ print("止损价", stop_loss_price)
+
+ property_val = float(data['info']['accFillSz'])
+ # 相同位数
+ print("可平仓资产", property_val)
+
+ # 止损价
+ # 止盈价
+ exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+ print('---------止损价 执行----------------------------------')
+
+ data = exchange.create_order(
+ 'DOT/USDT', 'limit', 'buy', property_val, closing_price, exchange_params)
+ print(data)
+ print(type(data))
+
+
+def testK_sell_close():
+ # 平多
+ data = exchange.create_limit_buy_order(
+ 'DOT/USDT', 1, 5, {"tdMode": "cross"})
+ print(data)
+ print(type(data))
+
+ # closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
+ # print(closed_orders)
+
+ # closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
+ # print(closed_orders)
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'long':
+ longRun()
+ elif func == 'run':
+ debug()
+ elif func == 'test':
+ testKdan()
+ elif func == 't_get_trade':
+ t_get_trade()
+ elif func == 't_buy_open':
+ testK_buy_open()
+ elif func == 't_buy_close':
+ testK_buy_close()
+ elif func == 't_sell_open':
+ testK_sell_open()
+ elif func == 't_sell_cloe':
+ testK_sell_close()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py b/plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py
new file mode 100644
index 000000000..d4fc36864
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py
@@ -0,0 +1,437 @@
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py run
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py long
+
+
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import json
+import pandas as pd
+# import pandas_ta as ta
+from pprint import pprint
+import numpy as np
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = common.initEx()
+exchange.load_markets()
+
+# 默认开仓数据
+# default_open_num = 10
+
+default_open = {
+ 'ETH/USDT': 30,
+ 'DOT/USDT': 30,
+ 'CEL/USDT': 30,
+}
+
+default_sell = {
+ 'ETH/USDT': 0.02,
+ 'DOT/USDT': 5,
+ 'CEL/USDT': 85,
+}
+
+
+# 做多开仓
+def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ common.writeLogEx('------做多----------------------------------', symbol)
+
+ default_open_num = default_open[symbol]
+ # 做多开仓 | 市价
+ data = exchange.createMarketBuyOrder(
+ symbol, default_open_num, {"tdMode": "cross"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ # 做多-止损价大于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 - 0.003))
+
+ # property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
+ property_val = common.addition(
+ order_data['info']['accFillSz'], order_data['info']['fee'])
+ property_val = float(property_val)
+
+ # 可平仓的数量
+ property_val = common.roundValCeil(
+ property_val, order_data['info']['accFillSz'])
+
+ common.writeLogEx('可平仓资产:' + str(property_val), symbol)
+
+ # 止盈价
+ diff = float(open_price) - float(stop_loss_price)
+ closing_price_c = float(open_price) + (diff * 2)
+ closing_price = float(open_price) * float((1 + profit))
+ # 选择盈利多的
+ if closing_price_c > closing_price:
+ closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, stop_loss_price)
+ # stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价/止盈价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ # 止损条件单
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ # 止赢条件单
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
+
+ common.writeLogEx('止赢数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做多 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做多开仓
+ # profit 百分比
+ try:
+ return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+ common.writeLogEx('------做空----------------------------------', symbol)
+
+ # 计算借币卖币多多少,以USDT为基准
+ # sell_num = float(default_open_num) / float(stop_loss_price)
+ # sell_num = round(sell_num, 8)
+
+ sell_num = default_sell[symbol]
+ # 做空开仓 | 市价
+ data = exchange.createMarketSellOrder(
+ symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
+
+ # 做空-止损价小于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 + 0.003))
+
+ # 止盈价
+ diff = float(stop_loss_price) - float(open_price)
+ closing_price_c = float(open_price) - (diff * 2)
+ closing_price = float(open_price) * float((1 - profit))
+ # 选择盈利多的
+ if closing_price_c < closing_price:
+ closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, open_price)
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ sl_amount = common.multiply(stop_loss_price, sell_num)
+ # 解决平仓时,未全部平仓
+ sl_amount = common.addition(sl_amount, 0.1)
+ common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ sl_amount), stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', sl_amount, stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ # 设置 -止盈价
+ tp_amount = common.multiply(closing_price, sell_num)
+ # 解决平仓时,未全部平仓
+ tp_amount = common.addition(tp_amount, 0.1)
+ common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ tp_amount), closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', tp_amount, closing_price, tp_exchange_params)
+
+ common.writeLogEx('止盈价数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做空 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做空开仓
+ # profit 百分比
+ try:
+ return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def getOnlineData(symbol, input_tf="15m", limit=230):
+ bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+ df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
+ df.set_index('dt', inplace=True)
+ df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
+ return df
+
+
+def isKdj(last, last_pre):
+ # 判断是否是金叉
+ if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
+ return True
+ return False
+
+
+def isDeadFork(last, last_pre):
+ # 判断是否是死叉
+ if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
+ return True
+ return False
+
+
+def getMACD(df, lenght=-60):
+ if len(df['close'].values) < 100:
+ return False, None
+
+ close_p = df['close'].values
+ df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
+ fastperiod=12,
+ slowperiod=26,
+ signalperiod=9)
+ return df[lenght:]
+
+
+def getEMA(df):
+ close_p = df['close'].values
+ df['ema'] = talib.EMA(np.array(close_p), timeperiod=200)
+ return df
+
+
+def doneMacd(data, tag, timeframe):
+ data = getEMA(data)
+
+ ma_data = getMACD(data)
+ # alen = len(data)
+ # print(ma_data)
+
+ t_data = ma_data.tail(3)
+ # print(t_data)
+
+ last_pre = t_data.iloc[0]
+ last = t_data.iloc[1]
+
+ # print(last)
+ # print(last.index)
+
+ # print('close:', last['close'], 'ema:', last['ema'])
+
+ obj = common.MsgTpl()
+ symbol = tag.upper() + '/USDT'
+ obj.setName(symbol)
+ obj.setStrategyName("MACD检查")
+ obj.setTimeFrame(timeframe)
+ obj.setOpenTime(last['timestamp'] / 1000)
+
+ now_data = t_data.iloc[2]
+ # print('now_data:', now_data)
+
+ # msg = obj.toText()
+ # print(msg)
+
+ if isKdj(last, last_pre) and last['close'] > last['ema']:
+ # if isKdj(last, last_pre):
+
+ # closing_price = common.calc_ClosingPrice(
+ # now_data['close'], last_pre['low'], 'buy')
+ # # print('closing_price:', closing_price)
+
+ # 做多止损点
+ stop_loss_price = last_pre['low']
+ buy_status, open_price, closing_price = onBuyOrder(
+ symbol, stop_loss_price, 0.005, timeframe)
+
+ if buy_status:
+ obj.setStrategicDt('buy')
+ # 做多止损点
+ obj.setStopLossPrice(str(stop_loss_price))
+ obj.setOpenPrice(str(open_price))
+ obj.setClosingPrice(str(closing_price))
+ obj.setContent("检查到金叉状态")
+ msg = obj.toText()
+ print(msg)
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+ if isDeadFork(last, last_pre) and last['close'] < last['ema']:
+ # if isDeadFork(last, last_pre):
+ # closing_price = common.calc_ClosingPrice(
+ # now_data['close'], last_pre['high'], 'sell')
+
+ # 做空止损点
+ stop_loss_price = last_pre['high']
+ sell_status, open_price, closing_price = onSellOrder(
+ symbol, stop_loss_price, 0.005, timeframe)
+ if sell_status:
+ obj.setStopLossPrice(str(stop_loss_price))
+ obj.setOpenPrice(str(open_price))
+ obj.setClosingPrice(str(closing_price))
+ obj.setStrategicDt('sell')
+ obj.setContent("检查到死叉状态")
+ msg = obj.toText()
+ print(msg)
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+
+def mainProcess(tag, timeframe='15m'):
+ symbol = tag.upper() + '/USDT'
+ data = getOnlineData(symbol, timeframe)
+
+ doneMacd(data, tag, timeframe)
+
+
+def foreachList():
+ tag_list = [
+ 'eth', 'dot'
+ ]
+
+ for tag in tag_list:
+ mainProcess(tag, '15m')
+ time.sleep(1)
+
+
+def longRun():
+ while True:
+ foreachList()
+ time.sleep(3)
+
+
+def debug():
+ while True:
+ mainProcess('xrp', '1m')
+ time.sleep(3)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'long':
+ longRun()
+ elif func == 'run':
+ debug()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/online_test.py b/plugins/cryptocurrency_trade/ccxt/strategy/online_test.py
new file mode 100644
index 000000000..3ac993513
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/online_test.py
@@ -0,0 +1,273 @@
+# cd /www/server/mdserver-web && source bin/activate
+
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_buy_open
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_buy_close
+
+
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_sell_open
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_sell_cloe
+
+# 获取仓位数据
+
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_get_trade
+
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import pandas as pd
+# import pandas_ta as ta
+from pprint import pprint
+import numpy as np
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = common.initEx()
+
+exchange.load_markets()
+
+
+def t_get_trade():
+ data = exchange.fetchPositions()
+ print(data)
+
+
+def btc_test():
+ closing_price = 24599.9
+ stop_loss_price = 24740.4
+
+ # ---------------------------
+ stop_loss_args = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+ print('---------止损价 执行 START ----------------------------------')
+
+ sl_amount = stop_loss_price * 0.003
+ print(amount)
+ data = exchange.create_order(
+ 'BTC/USDT', 'limit', 'buy', sl_amount, stop_loss_price, stop_loss_args)
+ print(data)
+ print('---------止损价 执行 END ----------------------------------')
+
+ print('---------止盈价 执行 START ----------------------------------')
+
+ closing_price_args = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ cp_amount = closing_price * 0.003
+ print(amount)
+ data = exchange.create_order(
+ 'BTC/USDT', 'limit', 'buy', cp_amount, closing_price, closing_price_args)
+ print(data)
+ print('---------止盈价 执行 END ----------------------------------')
+
+
+def testK_buy_open_sz():
+ # 做多开仓
+
+ data = exchange.fetchTicker('DOT/USDT')
+ print(data)
+ # data = exchange.createMarketBuyOrder('DOT/USDT', 1, {"tdMode": "cross"})
+ # print(data)
+ # print(type(data))
+
+
+def testK_buy_open():
+ # 做多开仓
+
+ data = exchange.createMarketBuyOrder(
+ 'DOT/USDT', 1, {"tdMode": "cross"})
+ print(data)
+ print(type(data))
+
+ # 数据
+ # {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
+
+ # 查询委托单
+ order_id = data['info']['ordId']
+ # order_id = '554437054223302656'
+ data = exchange.fetchOrder(order_id, 'DOT/USDT')
+ print(data['info'])
+ # {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
+
+ open_price = data['info']['avgPx']
+
+ print("开仓平均价", open_price)
+
+ # 止盈价
+ closing_price = float(open_price) * float((1 + 0.005))
+ closing_price = common.roundVal(closing_price, open_price)
+ print("止盈价", closing_price)
+
+ # 止损价
+ stop_loss_price = float(open_price) * float((1 - 0.01))
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ print("止损价", stop_loss_price)
+
+ property_val = float(data['info']['accFillSz']) + \
+ float(data['info']['fee'])
+
+ # 相同位数
+ property_val = common.roundValCeil(property_val, data['info']['accFillSz'])
+ print("可平仓资产", property_val)
+
+ # closed_orders = exchange.fetchClosedOrders('DOT/USDT', limit=2)
+ # print('closed_orders', closed_orders)
+
+ # print(exchange.fetchBalance())
+
+ # 可以用,限价单
+ # time.sleep(1)
+ # data = exchange.createLimitSellOrder(
+ # 'DOT/USDT', property_val, closing_price, {"tdMode": "cross", 'ccy': 'USDT', "reduceOnly": True})
+ # print(data)
+ # print(type(data))
+
+ # 止损价
+ # 止盈价
+
+ exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+ print('---------止损价 执行----------------------------------')
+
+ data = exchange.create_order(
+ 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
+ print(data)
+ print(type(data))
+
+ # print('---------止盈价 执行----------------------------------')
+
+ # exchange_params = {
+ # 'stopPrice': closing_price,
+ # 'type': 'stopLimit',
+ # }
+ # data = exchange.create_order(
+ # 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
+ # print(data)
+ # print(type(data))
+
+
+def testK_buy_close():
+ # 平多
+ data = exchange.create_limit_buy_order(
+ 'DOT/USDT', 1, 5, {"tdMode": "cross"})
+ print(data)
+ print(type(data))
+
+ # closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
+ # print(closed_orders)
+
+ # closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
+ # print(closed_orders)
+
+
+def testK_sell_open():
+ # 做空开仓
+
+ # data = exchange.createMarketSellOrder(
+ # 'DOT/USDT', 1, {"tdMode": "cross", 'ccy': "USDT", })
+ # print(data)
+ # print(type(data))
+
+ # 数据
+ # {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
+
+ # # 查询委托单
+ # order_id = data['info']['ordId']
+ order_id = '554516809819832320'
+ data = exchange.fetchOrder(order_id, 'DOT/USDT')
+ print(data['info'])
+ # {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
+
+ open_price = data['info']['avgPx']
+
+ print("开仓平均价", open_price)
+
+ # 止盈价
+ closing_price = float(open_price) * float((1 - 0.005))
+ closing_price = common.roundVal(closing_price, open_price)
+ print("止盈价", closing_price)
+
+ # 止损价
+ stop_loss_price = float(open_price) * float((1 + 0.01))
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ print("止损价", stop_loss_price)
+
+ property_val = float(data['info']['accFillSz'])
+ # 相同位数
+ print("可平仓资产", property_val)
+
+ # 止损价
+ # 止盈价
+ exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+ print('---------止损价 执行----------------------------------')
+
+ data = exchange.create_order(
+ 'DOT/USDT', 'limit', 'buy', property_val, closing_price, exchange_params)
+ print(data)
+ print(type(data))
+
+
+def testK_sell_close():
+ # 平多
+ data = exchange.create_limit_buy_order(
+ 'DOT/USDT', 1, 5, {"tdMode": "cross"})
+ print(data)
+ print(type(data))
+
+ # closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
+ # print(closed_orders)
+
+ # closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
+ # print(closed_orders)
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'long':
+ longRun()
+ elif func == 'run':
+ debug()
+ elif func == 'test':
+ testKdan()
+ elif func == 't_get_trade':
+ t_get_trade()
+ elif func == 't_buy_open':
+ btc_test()
+ elif func == 't_buy_close':
+ testK_buy_close()
+ elif func == 't_sell_open':
+ testK_sell_open()
+ elif func == 't_sell_cloe':
+ testK_sell_close()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/rsi_robot.py b/plugins/cryptocurrency_trade/ccxt/strategy/rsi_robot.py
new file mode 100644
index 000000000..9a4129c1e
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/rsi_robot.py
@@ -0,0 +1,102 @@
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import pandas as pd
+import pandas_ta as ta
+from pprint import pprint
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/rsi_robot.py run
+
+
+pd.set_option('display.max_rows', None)
+
+exchange = common.initEx()
+
+# 查看隐形方法
+# print(dir(exchange))
+
+
+exchange.load_markets()
+
+entry_rsi = 30
+exit_rsi = 40
+
+
+symbol = 'XRP/USDT'
+timeframe = '15m'
+
+tf_mult = exchange.parse_timeframe(timeframe) * 1000
+
+
+def indicators(data):
+
+ data['rsi'] = data.ta.rsi(length=10)
+ data['ema'] = data.ta.ema(length=200)
+
+ # close_p = data['close'].values
+ # data['rsi'] = talib.RSI(close_p, timeperiod=10)
+ # data['ema'] = talib.EMA(close_p, timeperiod=200)
+ return data
+
+
+def check_buy_sell_signals(df):
+ last_row_index = len(df.index) - 1
+ lastest_rsi = round(df['rsi'].iloc[-1], 2)
+ lastest_price = round(df['close'].iloc[-1], 5)
+ lastest_ema = round(df['ema'].iloc[-1], 5)
+ lastest_ts = df['timestamp'].iloc[-1]
+
+ msg = "lastest_rsi:" + str(lastest_rsi) + " < entry_rsi:" + str(entry_rsi)
+ msg += ",lastest_price:" + \
+ str(lastest_price) + " > lastest_ema:" + str(lastest_ema)
+ print(msg)
+
+ long_cond = (lastest_rsi < entry_rsi) and (lastest_price > lastest_ema)
+ if long_cond:
+ print("买入")
+ order = exchange.create_market_buy_order(symbol, 1)
+
+ closed_orders = exchange.fetchClosedOrders(symbol, limit=2)
+ if len(closed_orders) > 0:
+ print("closed_orders:", closed_orders)
+ most_recent_closed_order = closed_orders[-1]
+ diff = lastest_ts - most_recent_closed_order['timestamp']
+ last_buy_signal_cnt = int(diff / tf_mult)
+
+ exit_cond = (lastest_rsi > exit_rsi) and (last_buy_signal_cnt > 10)
+ if exit_cond:
+ print("卖出")
+ order = exchange.create_market_sell_order(symbol, 1)
+ return
+
+
+def runBot():
+ bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=200)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+ df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
+
+ df = indicators(df).tail(30)
+
+ check_buy_sell_signals(df)
+
+
+def longRunBot():
+ while True:
+ runBot()
+ time.sleep(10)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'run':
+ longRunBot()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py b/plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py
new file mode 100644
index 000000000..a51348a68
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py
@@ -0,0 +1,536 @@
+# cd /www/server/mdserver-web && source bin/activate
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py run
+# python3 plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py long
+
+
+# 动量策略交易
+################
+
+
+import ccxt
+import talib
+
+import sys
+import os
+import time
+import json
+import pandas as pd
+# import pandas_ta as ta
+from pprint import pprint
+import numpy as np
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
+import common
+
+sys.path.append(os.getcwd() + "/class/core")
+import mw
+
+exchange = common.initEx()
+exchange.load_markets()
+
+# 默认开仓数据
+# default_open_num = 70
+# default_sell_num = 0.003
+
+default_open = {
+ 'BTC/USDT': 70,
+ 'XRP/USDT': 70,
+}
+
+default_sell = {
+ 'BTC/USDT': 0.003,
+ 'XRP/USDT': 185,
+}
+
+
+# 做多开仓
+def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+
+ common.writeLogEx('------做多----------------------------------', symbol)
+
+ default_open_num = default_open[symbol]
+ # 做多开仓 | 市价
+ data = exchange.createMarketBuyOrder(
+ symbol, default_open_num, {"tdMode": "cross"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ # 修正小数点位数
+ open_price = common.roundVal(open_price, stop_loss_price)
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+
+ # 做多-止损价大于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 - 0.003))
+
+ # property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
+
+ property_val = common.addition(order_data['info'][
+ 'accFillSz'], order_data['info']['fee'])
+ property_val = float(property_val)
+ # 可平仓的数量
+ # property_val = common.roundValCeil(
+ # property_val, order_data['info']['accFillSz'])
+
+ common.writeLogEx('可平仓资产:' + str(property_val), symbol)
+
+ # 止盈价
+ diff = float(open_price) - float(stop_loss_price)
+ closing_price = float(open_price) + (diff * 1.5)
+ # closing_price = float(open_price) * float((1 + profit))
+ # # 选择盈利多的
+ # if closing_price_c > closing_price:
+ # closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, open_price)
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价/止盈价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ # 止损条件单
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ # 止赢条件单
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
+ property_val, closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
+
+ common.writeLogEx('止盈数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做多 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做多开仓
+ # profit 百分比
+ try:
+ return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+
+ # 信号只在一个周期内执行一次|start
+ lock_file = common.getServerDir() + '/signal.json'
+ if not os.path.exists(lock_file):
+ mw.writeFile(lock_file, '{}')
+
+ stype = symbol.replace('/', '_') + '_' + timeframe
+ trigger_time = common.toUnixTimeSecond(timeframe)
+ lock_data = json.loads(mw.readFile(lock_file))
+ if stype in lock_data:
+ diff_time = time.time() - lock_data[stype]['do_time']
+ if diff_time >= trigger_time:
+ lock_data[stype]['do_time'] = time.time()
+ else:
+ return False, 0, 0
+ else:
+ lock_data[stype] = {'do_time': time.time()}
+
+ mw.writeFile(lock_file, json.dumps(lock_data))
+ # 信号只在一个周期内执行一次|end
+ common.writeLogEx('------做空----------------------------------', symbol)
+
+ # 计算借币卖币多多少,以USDT为基准
+ # sell_num = float(default_open_num) / float(stop_loss_price)
+ # sell_num = round(sell_num, 8)
+ # sell_num = default_sell_num
+ sell_num = default_sell[symbol]
+
+ # 做空开仓 | 市价
+ data = exchange.createMarketSellOrder(
+ symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
+
+ common.writeLogEx('开仓数据:', symbol)
+ common.writeLogEx(json.dumps(data), symbol)
+
+ order_id = data['info']['ordId']
+ order_data = exchange.fetchOrder(order_id, symbol)
+
+ common.writeLogEx('订单数据:', symbol)
+ common.writeLogEx(json.dumps(order_data), symbol)
+
+ # 实际开场平均价
+ open_price = order_data['info']['avgPx']
+
+ # 修正
+ open_price = common.roundVal(open_price, stop_loss_price)
+
+ common.writeLogEx('实际开仓价:' + str(open_price), symbol)
+ common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
+
+ # 做空-止损价小于开仓价,重设止损价
+ if float(stop_loss_price) <= float(open_price):
+ stop_loss_price = float(open_price) * float((1 + 0.003))
+
+ # 止盈价
+ diff = float(stop_loss_price) - float(open_price)
+ closing_price = float(open_price) - (diff * 1.5)
+ # closing_price = float(open_price) * float((1 - profit))
+ # 选择盈利多的
+ # if closing_price_c < closing_price:
+ # closing_price = closing_price_c
+
+ closing_price = common.roundVal(closing_price, open_price)
+ stop_loss_price = common.roundVal(stop_loss_price, open_price)
+
+ common.writeLogEx('止盈价:' + str(closing_price), symbol)
+ common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
+
+ # 设置 - 止损价
+ sl_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'slOrdPx': "-1",
+ 'slTriggerPx': stop_loss_price,
+ }
+
+ sl_amount = common.multiply(stop_loss_price, sell_num)
+ # 解决平仓时,未全部平仓
+ sl_amount = common.addition(sl_amount, 0.1)
+ common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
+ common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ sl_amount), stop_loss_price, sl_exchange_params]), symbol)
+ sl_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', float(sl_amount), stop_loss_price, sl_exchange_params)
+
+ common.writeLogEx('止损价数据:', symbol)
+ common.writeLogEx(json.dumps(sl_cond_data), symbol)
+
+ tp_exchange_params = {
+ 'ccy': "USDT",
+ 'reduceOnly': True,
+ 'tdMode': "cross",
+ 'tpOrdPx': "-1",
+ 'tpTriggerPx': closing_price,
+ }
+
+ # 设置 -止盈价
+ # tp_amount = closing_price * sell_num
+ tp_amount = common.multiply(closing_price, sell_num)
+ # 解决平仓时,未全部平仓
+ tp_amount = common.addition(tp_amount, 0.1)
+ common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
+ common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
+ tp_amount), closing_price, tp_exchange_params]), symbol)
+ tp_cond_data = exchange.create_order(
+ symbol, 'limit', 'buy', float(tp_amount), closing_price, tp_exchange_params)
+
+ common.writeLogEx('止盈价数据:', symbol)
+ common.writeLogEx(json.dumps(tp_cond_data), symbol)
+
+ common.writeLogEx('------做空 end----------------------------------', symbol)
+ return True, open_price, closing_price
+
+
+def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
+ # 做空开仓
+ # profit 百分比
+ try:
+ return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
+ except Exception as e:
+ common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
+ return False, 0, 0
+
+
+def getOnlineData(symbol, input_tf="15m", limit=500):
+ bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=500)
+ df = pd.DataFrame(bars[:], columns=['timestamp',
+ 'open', 'high', 'low', 'close', 'volume'])
+ df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
+ df.set_index('dt', inplace=True)
+ df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
+ return df
+
+
+def isKdj(last, last_pre):
+ # 判断是否是金叉
+ if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
+ return True
+ return False
+
+
+def isDeadFork(last, last_pre):
+ # 判断是否是死叉
+ if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
+ return True
+ return False
+
+
+def getTarget(df):
+ close = df['close'].values
+
+ # vega
+ df['ema144'] = talib.EMA(np.array(close), timeperiod=144)
+ df['ema169'] = talib.EMA(np.array(close), timeperiod=169)
+
+ df['ema288'] = talib.EMA(np.array(close), timeperiod=288)
+ df['ema388'] = talib.EMA(np.array(close), timeperiod=388)
+
+ df['rsi'] = talib.RSI(close, timeperiod=14)
+ return df
+
+
+# 多头信号
+def isBuyCrondSignal(last):
+ if last['ema'] > last['ma'] and (last['rsi'] > 50 and last['rsi'] < 70) and last['low'] >= last['ma'] and last['close'] > last['open']:
+ return True
+ return False
+
+
+def isBuyFristSignal(data):
+ data = data.sort_values(by=['timestamp'], ascending=False)
+ # print(data)
+ data_len = len(data)
+ signal_num = 0
+
+ first_data = data.iloc[1]
+ # print(1, first_data['close'], first_data['ema'])
+ is_buy_signal = isBuyCrondSignal(first_data)
+
+ for x in range(2, data_len):
+ tmp = data.iloc[x]
+ # print(data.iloc[x])
+ # print(x, tmp['close'], tmp['ema'])
+ if isBuyCrondSignal(tmp):
+ signal_num = + 1
+
+ # print('signal_num:', signal_num)
+
+ if (tmp['ema'] < tmp['ma']):
+ break
+
+ if str(tmp['ema']) == 'nan':
+ break
+
+ print("is_buy_signal:", is_buy_signal, 'signal_num:', signal_num)
+ if is_buy_signal and signal_num == 0:
+ return True
+
+ return False
+
+
+# 空头信号
+def isSellCrondSignal(last):
+ if last['ema'] < last['ma'] and (last['rsi'] > 30 and last['rsi'] < 50) and last['high'] <= last['ema'] and last['close'] < last['open']:
+ return True
+ return False
+
+
+def isSellFristSignal(data):
+ data = data.sort_values(by=['timestamp'], ascending=False)
+ # print(data)
+ data_len = len(data)
+ signal_num = 0
+
+ first_data = data.iloc[1]
+ # print(1, first_data['close'], first_data['ema'])
+ is_sell_signal = isSellCrondSignal(first_data)
+
+ for x in range(2, data_len):
+ tmp = data.iloc[x]
+ # print(data.iloc[x])
+ # print(x, tmp['close'], tmp['ema'])
+ if isSellCrondSignal(tmp):
+ signal_num = + 1
+
+ # print('signal_num:', signal_num)
+
+ if (tmp['ema'] < tmp['ma']):
+ break
+
+ if str(tmp['ema']) == 'nan':
+ break
+
+ print("is_sell_signal:", is_sell_signal, 'signal_num:', signal_num)
+ if is_sell_signal and signal_num == 0:
+ return True
+
+ return False
+
+
+def isDownTrendCover(data, tag, timeframe):
+ # print(tag, timeframe, "开始检测是否下跌趋势")
+ # 下跌趋势回调
+ key_data = data.tail(2)
+ last_pre = key_data.iloc[0]
+ last = key_data.iloc[1]
+
+ pdata = data.tail(11)
+
+ for x in range(10):
+ t = pdata.iloc[x]
+ if t['low'] > t['ema388']:
+ print(tag, timeframe, "上升震荡行情过滤")
+ # print(t)
+ return False
+
+ if last['ema144'] > last['ema388']:
+ return False
+
+ print(tag, timeframe, "检查是下跌趋势!")
+ # 检查是否是下跌趋势
+ if last['high'] > last['ema388']:
+ return True
+
+ return False
+
+
+def isUpTrendAndCover(data, tag, timeframe):
+ # print(tag, timeframe, "开始检测是否上升趋势")
+ # 上升趋势回调
+ key_data = data.tail(2)
+ # print(key_data)
+ last_pre = key_data.iloc[0]
+ last = key_data.iloc[1]
+
+ pdata = data.tail(11)
+
+ for x in range(10):
+ t = pdata.iloc[x]
+ if t['low'] < t['ema144']:
+ print(tag, timeframe, "上升震荡行情过滤")
+ return False
+
+ if last['ema144'] < last['ema388']:
+ return False
+
+ print(tag, timeframe, "检查是上升趋势")
+
+ # 检查是否是上升趋势
+ if last['low'] < last['ema144']:
+ return True
+ return False
+
+
+def vegaTrade(data, tag, timeframe):
+ data = getTarget(data)
+ # print(data)
+
+ key_data = data.tail(2)
+ # print(key_data)
+ # last_pre = key_data.iloc[0]
+ last = key_data.iloc[1]
+ # print(last)
+
+ obj = common.MsgTpl()
+ symbol = tag.upper() + '/USDT'
+ obj.setName(symbol)
+ obj.setStrategyName("VEGA交易策略|仅具有指导作用")
+ obj.setTimeFrame(timeframe)
+ obj.setOpenTime(last['addtime'])
+
+ if isUpTrendAndCover(data, tag, timeframe):
+ obj.setStrategicDt('buy')
+ obj.setContent("VEGA上升趋势!回调做多~建议!")
+
+ msg = obj.toText()
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+ if isDownTrendCover(data, tag, timeframe):
+ obj.setStrategicDt('sell')
+ obj.setContent("Vega下跌趋势!回调做空~建议!")
+ msg = obj.toText()
+ common.notifyMsg(msg, timeframe, tag)
+ common.writeLog(msg)
+
+
+def mainProcess(tag, timeframe='5m'):
+ symbol = tag.upper() + '/USDT'
+ data = common.getDataFromDb_DF(timeframe, tag.lower())
+ vegaTrade(data, tag, timeframe)
+ return True
+
+
+def foreachList():
+ tag_list = ['btc', 'xrp', 'eth', 'ltc', 'okb']
+ for tag in tag_list:
+ trame_list = ['5m', '15m', '1h', '4h']
+ for tf in trame_list:
+ mainProcess(tag, tf)
+ # time.sleep(1)
+
+
+def longRun():
+ while True:
+ try:
+ foreachList()
+ time.sleep(1)
+ except Exception as e:
+ print(mw.getTracebackInfo())
+ time.sleep(3)
+
+
+def debug():
+ while True:
+ mainProcess('btc', '5m')
+ time.sleep(1)
+
+
+if __name__ == "__main__":
+ func = sys.argv[1]
+ if func == 'long':
+ longRun()
+ elif func == 'run':
+ debug()
+ else:
+ print('error')
diff --git a/plugins/cryptocurrency_trade/ccxt/test/p1.py b/plugins/cryptocurrency_trade/ccxt/test/p1.py
new file mode 100644
index 000000000..5ee453968
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/test/p1.py
@@ -0,0 +1,128 @@
+'''
+pip install git+https://github.com/catalyst-team/catalyst@master --upgrade
+
+
+cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
+
+
+cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
+
+python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
+'''
+
+from datetime import datetime
+import akshare as ak
+import pandas as pd
+import backtrader as bt
+
+
+class BollStrategy(bt.Strategy): # BOLL策略程序
+ params = (("nk", 13), # 求均值的天数
+ ('printlog', False),) # 打印log
+
+ def __init__(self): # 初始化
+ self.data_close = self.datas[0].close # 指定价格序列
+ # 初始化交易指令、买卖价格和手续费
+ self.order = None
+ self.buy_price = None
+ self.buy_comm = None
+ # Boll指标计算
+ self.top = bt.indicators.BollingerBands(
+ self.datas[0], period=self.params.nk).top
+ self.bot = bt.indicators.BollingerBands(
+ self.datas[0], period=self.params.nk).bot
+ # 添加移动均线指标
+ self.sma = bt.indicators.SimpleMovingAverage(
+ self.datas[0], period=self.params.nk)
+
+ def next(self): # 买卖策略
+ if self.order: # 检查是否有指令等待执行
+ return
+ # 检查是否持仓
+ """
+ if not self.position: # 没有持仓
+ if self.data_close[0] > self.sma[0]: # 执行买入条件判断:收盘价格上涨突破20日均线
+ self.order = self.buy(size=100) # 执行买入
+ else:
+ if self.data_close[0] < self.sma[0]: # 执行卖出条件判断:收盘价格跌破20日均线
+ self.order = self.sell(size=100) # 执行卖出
+ """
+ if not self.position: # 没有持仓
+ if self.data_close[0] < self.bot[0]: # 收盘价格跌破下轨
+ self.log("BUY CREATE, %.2f" % self.data_close[0])
+ self.order = self.buy() # 执行买入
+ else:
+ if self.data_close[0] > self.top[0]: # 收盘价格上涨突破上轨
+ self.log("SELL CREATE, %.2f" % self.data_close[0])
+ self.order = self.sell() # 执行卖出
+
+ def log(self, txt, dt=None, do_print=False): # 日志函数
+ if self.params.printlog or do_print:
+ dt = dt or self.datas[0].datetime.date(0)
+ print('%s, %s' % (dt.isoformat(), txt))
+
+ def notify_order(self, order): # 记录交易执行情况
+ # 如果order为submitted/accepted,返回空
+ if order.status in [order.Submitted, order.Accepted]:
+ return
+ # 指令为buy/sell,报告价格结果
+ if order.status in [order.Completed]:
+ if order.isbuy():
+ self.log(
+ f"买入:\n价格:{order.executed.price},\
+ 成本:{order.executed.value},\
+ 手续费:{order.executed.comm}"
+ )
+ self.buyprice = order.executed.price
+ self.buycomm = order.executed.comm
+ else:
+ self.log(
+ f"卖出:\n价格:{order.executed.price},\
+ 成本: {order.executed.value},\
+ 手续费{order.executed.comm}"
+ )
+ self.bar_executed = len(self)
+
+ elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+ self.log("交易失败") # 指令取消/交易失败, 报告结果
+ self.order = None
+
+ def notify_trade(self, trade): # 记录交易收益情况
+ if not trade.isclosed:
+ return
+ self.log(f"策略收益:\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}")
+
+ def stop(self): # 回测结束后输出结果
+ self.log("(BOLL线: %2d日) 期末总资金 %.2f" %
+ (self.params.nk, self.broker.getvalue()), do_print=True)
+
+code = "600036" # 股票代码
+start_cash = 1000000 # 初始自己为1000000
+stake = 100 # 单次交易数量为1手
+commfee = 0.0005 # 佣金为万5
+sdate = '20210101' # 回测时间段
+edate = '20220930'
+cerebro = bt.Cerebro() # 创建回测系统实例
+# 利用AKShare获取股票的前复权数据的前6列
+df_qfq = ak.stock_zh_a_hist(
+ symbol=code, adjust="qfq", start_date=sdate, end_date=edate).iloc[:, :6]
+# 处理字段命名,以符合Backtrader的要求
+df_qfq.columns = ['date', 'open', 'close', 'high', 'low', 'volume', ]
+# 把date作为日期索引,以符合Backtrader的要求
+df_qfq.index = pd.to_datetime(df_qfq['date'])
+start_date = datetime.strptime(sdate, "%Y%m%d") # 转换日期格式
+end_date = datetime.strptime(edate, "%Y%m%d")
+# start_date=datetime(2022,1,4)
+# end_date=datetime(2022,9,16)
+data = bt.feeds.PandasData(
+ dataname=df_qfq, fromdate=start_date, todate=end_date) # 规范化数据格式
+cerebro.adddata(data) # 加载数据
+cerebro.addstrategy(BollStrategy, nk=13, printlog=True) # 加载交易策略
+cerebro.broker.setcash(start_cash) # broker设置资金
+cerebro.broker.setcommission(commission=commfee) # broker手续费
+cerebro.addsizer(bt.sizers.FixedSize, stake=stake) # 设置买入数量
+print("期初总资金: %.2f" % start_cash)
+cerebro.run() # 运行回测
+end_value = cerebro.broker.getvalue() # 获取回测结束后的总资金
+print("期末总资金: %.2f" % end_value)
+cerebro.plot()
diff --git a/plugins/cryptocurrency_trade/ccxt/test/p2.py b/plugins/cryptocurrency_trade/ccxt/test/p2.py
new file mode 100644
index 000000000..e5b84b8f5
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/test/p2.py
@@ -0,0 +1,154 @@
+'''
+pip install git+https://github.com/catalyst-team/catalyst@master --upgrade
+
+
+cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
+
+
+cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
+
+python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
+'''
+
+from datetime import datetime
+import akshare as ak
+import pandas as pd
+import backtrader as bt
+
+
+import sys
+import os
+
+sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/ccxt/strategy")
+import common
+
+
+class BollStrategy(bt.Strategy): # BOLL策略程序
+ params = (("nk", 13), # 求均值的天数
+ ('printlog', False),) # 打印log
+
+ def __init__(self): # 初始化
+ self.data_close = self.datas[0].close # 指定价格序列
+ # 初始化交易指令、买卖价格和手续费
+ self.order = None
+ self.buy_price = None
+ self.buy_comm = None
+ # Boll指标计算
+ self.top = bt.indicators.BollingerBands(
+ self.datas[0], period=self.params.nk).top
+ self.bot = bt.indicators.BollingerBands(
+ self.datas[0], period=self.params.nk).bot
+ # 添加移动均线指标
+ self.sma = bt.indicators.SimpleMovingAverage(
+ self.datas[0], period=self.params.nk)
+
+ def next(self): # 买卖策略
+ if self.order: # 检查是否有指令等待执行
+ return
+ # 检查是否持仓
+ """
+ if not self.position: # 没有持仓
+ if self.data_close[0] > self.sma[0]: # 执行买入条件判断:收盘价格上涨突破20日均线
+ self.order = self.buy(size=100) # 执行买入
+ else:
+ if self.data_close[0] < self.sma[0]: # 执行卖出条件判断:收盘价格跌破20日均线
+ self.order = self.sell(size=100) # 执行卖出
+ """
+ if not self.position: # 没有持仓
+ if self.data_close[0] < self.bot[0]: # 收盘价格跌破下轨
+ self.log("BUY CREATE, %.2f" % self.data_close[0])
+ self.order = self.buy() # 执行买入
+ else:
+ if self.data_close[0] > self.top[0]: # 收盘价格上涨突破上轨
+ self.log("SELL CREATE, %.2f" % self.data_close[0])
+ self.order = self.sell() # 执行卖出
+
+ def log(self, txt, dt=None, do_print=False): # 日志函数
+ if self.params.printlog or do_print:
+ dt = dt or self.datas[0].datetime.date(0)
+ print('%s, %s' % (dt.isoformat(), txt))
+
+ def notify_order(self, order): # 记录交易执行情况
+ # 如果order为submitted/accepted,返回空
+ if order.status in [order.Submitted, order.Accepted]:
+ return
+ # 指令为buy/sell,报告价格结果
+ if order.status in [order.Completed]:
+ if order.isbuy():
+ self.log(
+ f"买入:\n价格:{order.executed.price},\
+ 成本:{order.executed.value},\
+ 手续费:{order.executed.comm}"
+ )
+ self.buyprice = order.executed.price
+ self.buycomm = order.executed.comm
+ else:
+ self.log(
+ f"卖出:\n价格:{order.executed.price},\
+ 成本: {order.executed.value},\
+ 手续费{order.executed.comm}"
+ )
+ self.bar_executed = len(self)
+
+ elif order.status in [order.Canceled, order.Margin, order.Rejected]:
+ self.log("交易失败") # 指令取消/交易失败, 报告结果
+ self.order = None
+
+ def notify_trade(self, trade): # 记录交易收益情况
+ if not trade.isclosed:
+ return
+ self.log(f"策略收益:\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}")
+
+ def stop(self): # 回测结束后输出结果
+ self.log("(BOLL线: %2d日) 期末总资金 %.2f" %
+ (self.params.nk, self.broker.getvalue()), do_print=True)
+
+code = "600036" # 股票代码
+start_cash = 1000000 # 初始自己为1000000
+stake = 100 # 单次交易数量为1手
+commfee = 0.0005 # 佣金为万5
+sdate = '20210101' # 回测时间段
+edate = '20220930'
+cerebro = bt.Cerebro() # 创建回测系统实例
+# 利用AKShare获取股票的前复权数据的前6列
+# df_qfq = ak.stock_zh_a_hist(
+# symbol=code, adjust="qfq", start_date=sdate, end_date=edate).iloc[:, :6]
+# 日期 开盘 收盘 最高 最低 成交量
+# 0 2021-01-04 40.46 40.40 41.01 39.25 1549523
+# 1 2021-01-05 39.99 39.41 40.03 38.67 1387177
+# 2 2021-01-06 39.33 41.38 41.43 39.23 1200646
+# 3 2021-01-07 41.52 43.13 43.20 41.49 1078322
+# 4 2021-01-08 43.52 43.83 44.25 43.04 1299595
+# .. ... ... ... ... ... ...
+# 420 2022-09-26 34.02 33.95 34.52 33.84 525491
+# 421 2022-09-27 33.89 33.90 34.05 33.44 475414
+# 422 2022-09-28 33.79 33.67 34.00 33.42 487299
+# 423 2022-09-29 33.99 33.15 34.32 33.00 617859
+# 424 2022-09-30 33.26 33.65 33.95 33.12 539717
+
+tag = 'btc'
+symbol = tag.upper() + '/USDT'
+df_qfq = common.getDataFromDb_DF('15m', tag.lower())
+print(df_qfq)
+
+# # 处理字段命名,以符合Backtrader的要求
+# df_qfq.columns = ['date', 'open', 'close', 'high', 'low', 'volume', ]
+# # 把date作为日期索引,以符合Backtrader的要求
+# df_qfq.index = pd.to_datetime(df_qfq['date'])
+# start_date = datetime.strptime(sdate, "%Y%m%d") # 转换日期格式
+# end_date = datetime.strptime(edate, "%Y%m%d")
+start_date = datetime(2023, 4, 6)
+end_date = datetime(2023, 4, 9)
+data = bt.feeds.PandasData(dataname=df_qfq, fromdate=start_date,
+ todate=end_date) # 规范化数据格式
+
+cerebro.adddata(data) # 加载数据
+cerebro.addstrategy(BollStrategy, nk=13, printlog=True) # 加载交易策略
+cerebro.broker.setcash(start_cash) # broker设置资金
+cerebro.broker.setcommission(commission=commfee) # broker手续费
+cerebro.addsizer(bt.sizers.FixedSize, stake=stake) # 设置买入数量
+print("期初总资金: %.2f" % start_cash)
+cerebro.run() # 运行回测
+end_value = cerebro.broker.getvalue() # 获取回测结束后的总资金
+print("期末总资金: %.2f" % end_value)
+# cerebro.plot(style='candle')
diff --git a/plugins/cryptocurrency_trade/ccxt/test/p_test.py b/plugins/cryptocurrency_trade/ccxt/test/p_test.py
new file mode 100644
index 000000000..df28468cf
--- /dev/null
+++ b/plugins/cryptocurrency_trade/ccxt/test/p_test.py
@@ -0,0 +1,61 @@
+# coding:utf-8
+
+import pandas as pd
+
+import matplotlib.pyplot as plt
+
+
+from catalyst import run_algorithm
+from catalyst.api import order, record, symbol
+
+'''
+cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
+
+
+cd /www/server/mdserver-web && source bin/activate && source activate catalys
+
+cd /Users/midoks/Desktop/mwdev/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p_test.py
+catalyst ingest-exchange -x binance -i btc_usdt -f minute
+
+cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p_test.py
+'''
+
+
+def initialize(context):
+ # 初始化
+ context.asset = symbol('btc_usdt')
+
+
+def handle_data(context, data):
+ # 循环策略
+ order(context.asset, 1)
+ record(btc=data.current(context.asset, 'price'))
+
+
+def analyze(context, perf):
+
+ print(perf.portfolio_value)
+
+ ax1 = plt.subplot(211)
+ perf.portfolio_value.plot(ax=ax1)
+
+ ax1.set_ylabel('portfolio value')
+
+ ax2 = plt.subplot(212, sharex=ax1)
+ perf.btc.plot(ax=ax2)
+ ax2.set_ylabel('bitcoin value')
+ plt.show()
+
+
+if __name__ == "__main__":
+ run_algorithm(
+ capital_base=10000,
+ data_frequency='daily',
+ initialize=initialize,
+ handle_data=handle_data,
+ analyze=analyze,
+ exchange_name='binance',
+ quote_currenty='usdt',
+ start=pd.to_datetime("2018-01-01", utc=True),
+ end=pd.to_datetime("2018-10-01", utc=True),
+ )
diff --git a/plugins/cryptocurrency_trade/conf/create.sql b/plugins/cryptocurrency_trade/conf/create.sql
new file mode 100644
index 000000000..19e087958
--- /dev/null
+++ b/plugins/cryptocurrency_trade/conf/create.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS `ct_xx1_xx2` (
+ `addtime` BIGINT(20) not NULL,
+ `open` float NOT NULL,
+ `high` float NOT NULL,
+ `low` float NOT NULL,
+ `close` float NOT NULL,
+ `vol` float NOT NULL,
+ UNIQUE KEY `addtime` (`addtime`)
+);
diff --git a/plugins/cryptocurrency_trade/conf/order.sql b/plugins/cryptocurrency_trade/conf/order.sql
new file mode 100644
index 000000000..9e7f14c95
--- /dev/null
+++ b/plugins/cryptocurrency_trade/conf/order.sql
@@ -0,0 +1,11 @@
+CREATE TABLE IF NOT EXISTS `ct_order_list` (
+ `id` BIGINT(20) not NULL,
+ 'strategy_name' varchar(50) NULL,
+ `symbol` varchar(50) NOT NULL,
+ `fee` float NOT NULL,
+ `price` float NOT NULL,
+ `closing_price` float NOT NULL comment '',
+ `profit` float NOT NULL,
+ `source` TEXT,
+ `addtime` BIGINT(20) not NULL
+);
diff --git a/plugins/cryptocurrency_trade/conf/sup_strategy.tpl b/plugins/cryptocurrency_trade/conf/sup_strategy.tpl
new file mode 100644
index 000000000..c19824932
--- /dev/null
+++ b/plugins/cryptocurrency_trade/conf/sup_strategy.tpl
@@ -0,0 +1,14 @@
+[program:{$NAME}]
+command=bash -c "cd {$RUN_ROOT} && source bin/activate && python3 {$ABS_FILE} long"
+directory={$RUN_ROOT}
+autorestart=true
+startsecs=3
+startretries=3
+stdout_logfile={$SUP_ROOT}/log/{$NAME}.out.log
+stderr_logfile={$SUP_ROOT}/log/{$NAME}.err.log
+stdout_logfile_maxbytes=2MB
+stderr_logfile_maxbytes=2MB
+user=root
+priority=999
+numprocs=1
+process_name=%(program_name)s
\ No newline at end of file
diff --git a/plugins/cryptocurrency_trade/conf/sup_task.tpl b/plugins/cryptocurrency_trade/conf/sup_task.tpl
new file mode 100644
index 000000000..fe0c99d7f
--- /dev/null
+++ b/plugins/cryptocurrency_trade/conf/sup_task.tpl
@@ -0,0 +1,14 @@
+[program:{$NAME}]
+command=bash -c "cd {$RUN_ROOT} && source bin/activate && python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long"
+directory={$RUN_ROOT}
+autorestart=true
+startsecs=3
+startretries=3
+stdout_logfile={$SUP_ROOT}/log/{$NAME}.out.log
+stderr_logfile={$SUP_ROOT}/log/{$NAME}.err.log
+stdout_logfile_maxbytes=2MB
+stderr_logfile_maxbytes=2MB
+user=root
+priority=999
+numprocs=1
+process_name=%(program_name)s
\ No newline at end of file
diff --git a/plugins/cryptocurrency_trade/cryptocurrency_trade.py b/plugins/cryptocurrency_trade/cryptocurrency_trade.py
new file mode 100644
index 000000000..c87d8e8b0
--- /dev/null
+++ b/plugins/cryptocurrency_trade/cryptocurrency_trade.py
@@ -0,0 +1,350 @@
+# coding:utf-8
+
+import sys
+import io
+import os
+import time
+import re
+import string
+import subprocess
+import json
+
+web_dir = os.getcwd() + "/web"
+if os.path.exists(web_dir):
+ sys.path.append(web_dir)
+ os.chdir(web_dir)
+
+import core.mw as mw
+
+app_debug = False
+if mw.isAppleSystem():
+ app_debug = True
+
+
+def getPluginName():
+ return 'cryptocurrency_trade'
+
+
+def getPluginDir():
+ return mw.getPluginDir() + '/' + getPluginName()
+
+
+def getServerDir():
+ return mw.getServerDir() + '/' + getPluginName()
+
+
+def contentReplace(content):
+ service_path = mw.getServerDir()
+ content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
+ content = content.replace('{$SERVER_PATH}', service_path)
+ content = content.replace(
+ '{$SERVER_APP}', service_path + '/' + getPluginName())
+ return content
+
+
+def getArgs():
+ args = sys.argv[2:]
+ tmp = {}
+ args_len = len(args)
+ if args_len == 1:
+ t = args[0].strip('{').strip('}')
+ if t.strip() == '':
+ tmp = []
+ else:
+ t = t.split(':', 1)
+ tmp[t[0]] = t[1]
+ tmp[t[0]] = t[1]
+ elif args_len > 1:
+
+ for i in range(len(args)):
+ t = args[i].split(':', 1)
+ tmp[t[0]] = t[1]
+ return tmp
+
+
+def checkArgs(data, ck=[]):
+ for i in range(len(ck)):
+ if not ck[i] in data:
+ return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
+ return (True, mw.returnJson(True, 'ok'))
+
+
+def isSqlError(mysqlMsg):
+ # 检测数据库执行错误
+ mysqlMsg = str(mysqlMsg)
+ if "MySQLdb" in mysqlMsg:
+ return mw.returnJson(False, 'MySQLdb组件缺失!
进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
+ if "2002," in mysqlMsg:
+ return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
+ if "2003," in mysqlMsg:
+ return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
+ if "using password:" in mysqlMsg:
+ return mw.returnJson(False, '数据库密码错误')
+ if "1045" in mysqlMsg:
+ return mw.returnJson(False, '连接错误!')
+ if "SQL syntax" in mysqlMsg:
+ return mw.returnJson(False, 'SQL语法错误!')
+ if "Connection refused" in mysqlMsg:
+ return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
+ if "1133," in mysqlMsg:
+ return mw.returnJson(False, '数据库用户不存在!')
+ if "1007," in mysqlMsg:
+ return mw.returnJson(False, '数据库已经存在!')
+ return None
+
+
+def getConfigData():
+ cfg_path = getServerDir() + "/data.cfg"
+ if not os.path.exists(cfg_path):
+ mw.writeFile(cfg_path, '{}')
+ t = mw.readFile(cfg_path)
+ return json.loads(t)
+
+
+def writeConf(data):
+ cfg_path = getServerDir() + "/data.cfg"
+ mw.writeFile(cfg_path, json.dumps(data))
+ return True
+
+
+def getDbConf():
+ data = getConfigData()
+ if 'db' in data:
+ return mw.returnJson(True, 'ok', data['db'])
+ return mw.returnJson(False, 'ok', {})
+
+
+def getUserConf():
+ data = getConfigData()
+ if 'user' in data:
+ return mw.returnJson(True, 'ok', data['user'])
+ return mw.returnJson(False, 'ok', {})
+
+
+def restartSup():
+ cmd = 'python3 plugins/supervisor/index.py restart'
+ mw.execShell(cmd)
+
+
+def restartSupDst(name):
+ cmd = 'python3 plugins/supervisor/index.py restart_job {"name":"' + \
+ name + '","status":"stop"}'
+ mw.execShell(cmd)
+
+
+def syncDataAddTaskUninstall():
+ sup_path = mw.getServerDir() + '/supervisor'
+ if not os.path.exists(sup_path):
+ return mw.returnJson(False, '需要安装并启动supervisor插件')
+
+ name = "ct_task"
+ sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
+ if os.path.exists(sup_task_dst):
+ mw.execShell('rm -rf ' + sup_task_dst)
+
+ restartSup()
+ return mw.returnJson(True, '删除同步数据任务成功!')
+
+
+def syncDataAddTaskInstall():
+ sup_path = mw.getServerDir() + '/supervisor'
+ if not os.path.exists(sup_path):
+ return mw.returnJson(False, '需要安装并启动supervisor插件')
+
+ name = "ct_task"
+ sup_task_tpl = getPluginDir() + '/conf/sup_task.tpl'
+ sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
+ content = mw.readFile(sup_task_tpl)
+ content = content.replace(
+ '{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
+ content = content.replace(
+ '{$SUP_ROOT}', sup_path)
+ content = content.replace(
+ '{$NAME}', name)
+
+ mw.writeFile(sup_task_dst, content)
+ restartSup()
+ return mw.returnJson(True, '添加同步数据任务成功!')
+
+
+def syncDataAddTask():
+ args = getArgs()
+ data_args = checkArgs(args, ['check'])
+ if not data_args[0]:
+ return data_args[1]
+
+ if args['check'] == "0":
+ return syncDataAddTaskUninstall()
+ return syncDataAddTaskInstall()
+
+
+def syncDataDelete():
+ args = getArgs()
+ data_args = checkArgs(args, ['token'])
+ if not data_args[0]:
+ return data_args[1]
+
+ del_token = args['token']
+ data = getConfigData()
+
+ if 'token' in data:
+ data['token'].remove(del_token)
+ writeConf(data)
+
+ return mw.returnJson(True, '删除成功!')
+
+
+# callback ---------------------------------- start
+def get_datasource_logs(args):
+ log_file = getServerDir() + '/logs/datasource.log'
+ if not os.path.exists(log_file):
+ return '暂无日志'
+ data = mw.getLastLine(log_file, 10)
+ return data
+
+
+def get_strategy_logs(args):
+ log_file = getServerDir() + '/logs/strategy.log'
+ if not os.path.exists(log_file):
+ return '暂无日志'
+ data = mw.getLastLine(log_file, 10)
+ return data
+
+
+def save_body(args):
+
+ path = args['path']
+ encoding = args['encoding']
+ data = args['data']
+
+ tag = args['tag']
+
+ if not os.path.exists(path):
+ return mw.returnData(False, '文件不存在')
+ try:
+ if encoding == 'ascii':
+ encoding = 'utf-8'
+
+ data = data.encode(
+ encoding, errors='ignore').decode(encoding)
+
+ fp = open(path, 'w+', encoding=encoding)
+ fp.write(data)
+ fp.close()
+
+ set_strategy_restart({'id': tag})
+ return mw.returnData(True, '文件保存成功')
+ except Exception as ex:
+ return mw.returnData(False, '文件保存错误:' + str(ex))
+
+
+def get_strategy_path(args):
+ abs_id = args['id']
+ name = "ct_strategy_" + abs_id
+
+ abs_file = get_strategy_absfile(abs_id)
+ return mw.returnData(True, abs_file)
+
+
+def set_strategy_restart(args):
+ sup_path = mw.getServerDir() + '/supervisor'
+ if not os.path.exists(sup_path):
+ return mw.returnData(False, '需要安装并启动supervisor插件')
+
+ abs_id = args['id']
+ name = "ct_strategy_" + abs_id
+
+ sup_strategy_dst = sup_path + '/conf.d/' + name + '.ini'
+
+ if not os.path.exists(sup_strategy_dst):
+ return mw.returnData(False, '策略任务' + abs_id + '未添加!')
+
+ restartSupDst(name)
+ return mw.returnData(True, '重启策略任务' + abs_id + '成功!')
+
+
+def set_strategy_status(args):
+ sup_path = mw.getServerDir() + '/supervisor'
+ if not os.path.exists(sup_path):
+ return mw.returnData(False, '需要安装并启动supervisor插件')
+
+ abs_id = args['id']
+ name = "ct_strategy_" + abs_id
+ sup_strategy_dst = sup_path + '/conf.d/' + name + '.ini'
+
+ if args['status'] == 'stop':
+ if os.path.exists(sup_strategy_dst):
+ os.remove(sup_strategy_dst)
+ restartSup()
+ return mw.returnData(True, '删除策略任务' + abs_id + '成功!')
+
+ abs_file = get_strategy_absfile(abs_id)
+ sup_strategy_tpl = getPluginDir() + '/conf/sup_strategy.tpl'
+
+ content = mw.readFile(sup_strategy_tpl)
+ content = content.replace(
+ '{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
+ content = content.replace(
+ '{$SUP_ROOT}', sup_path)
+ content = content.replace(
+ '{$NAME}', name)
+ content = content.replace(
+ '{$ABS_FILE}', abs_file)
+
+ mw.writeFile(sup_strategy_dst, content)
+ restartSup()
+ return mw.returnData(True, '添加策略任务' + abs_id + '成功!')
+
+
+def get_strategy_absfile(abs_id):
+ info = getPluginDir() + '/ccxt/strategy/info.json'
+ info = json.loads(mw.readFile(info))
+
+ path = getPluginDir() + '/ccxt/strategy'
+ for x in range(len(info)):
+ if info[x]['id'] == abs_id:
+ return path + '/' + info[x]['file']
+
+ return path + '/abs.py'
+
+
+def get_strategy_list(args):
+ info = getPluginDir() + '/ccxt/strategy/info.json'
+ info = json.loads(mw.readFile(info))
+
+ st_path = mw.getServerDir() + '/supervisor/conf.d'
+
+ page = 1
+ page_size = 5
+ search = ''
+ data = {}
+ if 'page' in args:
+ page = int(args['page'])
+
+ if 'page_size' in args:
+ page_size = int(args['page_size'])
+
+ dlist_sum = len(info)
+
+ page_start = int((page - 1) * page_size)
+ page_end = page_start + page_size
+
+ if page_end >= dlist_sum:
+ ret_data = info[page_start:]
+ else:
+ ret_data = info[page_start:page_end]
+
+ for x in range(len(ret_data)):
+ strategy_dst = st_path + '/ct_strategy_' + ret_data[x]['id'] + '.ini'
+ if os.path.exists(strategy_dst):
+ ret_data[x]['status'] = 'start'
+ else:
+ ret_data[x]['status'] = 'stop'
+
+ data['data'] = ret_data
+ data['args'] = args
+ data['list'] = mw.getPage(
+ {'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'getStrategyList'})
+
+ return data
+# callback ---------------------------------- end
diff --git a/plugins/cryptocurrency_trade/ico.png b/plugins/cryptocurrency_trade/ico.png
new file mode 100644
index 000000000..91a234ba1
Binary files /dev/null and b/plugins/cryptocurrency_trade/ico.png differ
diff --git a/plugins/cryptocurrency_trade/index.html b/plugins/cryptocurrency_trade/index.html
new file mode 100755
index 000000000..88ca8ef2e
--- /dev/null
+++ b/plugins/cryptocurrency_trade/index.html
@@ -0,0 +1,26 @@
+
+
当前没有日志.+
序列 | +说明 | +状态 | +操作 | +
---|
当前没有日志.+
数据库地址
' + mlist += '数据库端口
' + mlist += '数据库名称
' + mlist += '用户名
' + mlist += '密码
' + + var option = '\ +交易所必填写[okex, binance]
' + mlist += 'apiKey必填写
' + mlist += 'secret必填写
' + mlist += 'password根据情况填写
' + mlist += 'uid根据情况填写
' + + var option = '\ +名称 | \ +操作 | \ + \ + '+ list +'\ +
---|