mirror of https://github.com/midoks/mdserver-web
parent
31ea0865d1
commit
b7efc0394b
@ -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)) |
||||||
|
|
||||||
|
|
||||||
|
``` |
@ -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组件缺失! <br>进入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') |
@ -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 |
@ -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) |
@ -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 |
@ -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) |
@ -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') |
@ -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') |
@ -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" |
||||||
|
} |
||||||
|
|
||||||
|
] |
@ -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') |
@ -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') |
@ -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') |
@ -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') |
@ -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') |
@ -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') |
@ -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') |
@ -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') |
@ -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() |
@ -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') |
@ -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), |
||||||
|
) |
@ -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`) |
||||||
|
); |
@ -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 |
||||||
|
); |
@ -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 |
@ -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 |
@ -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组件缺失! <br>进入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 |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,26 @@ |
|||||||
|
<style type="text/css"> |
||||||
|
.conf_p span{width: 100px;} |
||||||
|
.code{padding: 20px 5px;border: 1px solid #e1e1e1;background: #fcfcfc;border-radius: 4px;line-height: 24px;} |
||||||
|
.code span {display: block;margin-left: 15px;margin-bottom: 0;} |
||||||
|
</style> |
||||||
|
<div class="bt-form"> |
||||||
|
<div class="bt-w-main"> |
||||||
|
<div class="bt-w-menu"> |
||||||
|
<p class="bgw" onclick="pluginService('cryptocurrency_trade');">服务</p> |
||||||
|
<p onclick="dbConf();">数据库配置</p> |
||||||
|
<p onclick="syncDataList();">同步数据配置</p> |
||||||
|
<p onclick="userConf();">账户配置</p> |
||||||
|
</div> |
||||||
|
<div class="bt-w-con pd15"> |
||||||
|
<div class="soft-man-con"></div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<script type="text/javascript"> |
||||||
|
|
||||||
|
// resetPluginWinWidth(480); |
||||||
|
resetPluginWinHeight(400); |
||||||
|
$.getScript( "/plugins/file?name=cryptocurrency_trade&f=static/js/cryptocurrency_trade.js",function(){ |
||||||
|
pluginService('cryptocurrency_trade'); |
||||||
|
}); |
||||||
|
</script> |
@ -0,0 +1,347 @@ |
|||||||
|
# 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组件缺失! <br>进入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 initDreplace(): |
||||||
|
log_dir = getServerDir() + '/logs' |
||||||
|
if not os.path.exists(log_dir): |
||||||
|
d = mw.execShell('mkdir -p ' + log_dir) |
||||||
|
|
||||||
|
data = getConfigData() |
||||||
|
data['token'] = ['btc'] |
||||||
|
writeConf(data) |
||||||
|
|
||||||
|
return True |
||||||
|
|
||||||
|
|
||||||
|
def status(): |
||||||
|
initDreplace() |
||||||
|
return 'start' |
||||||
|
|
||||||
|
|
||||||
|
def start(): |
||||||
|
syncDataAddTaskInstall() |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def stop(): |
||||||
|
syncDataAddTaskUninstall() |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
def op(): |
||||||
|
return 'ok' |
||||||
|
|
||||||
|
|
||||||
|
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 setDbConf(): |
||||||
|
args = getArgs() |
||||||
|
data_args = checkArgs(args, ['db_host', 'db_port', |
||||||
|
'db_name', 'db_user', 'db_pass']) |
||||||
|
if not data_args[0]: |
||||||
|
return data_args[1] |
||||||
|
|
||||||
|
data = getConfigData() |
||||||
|
data['db'] = args |
||||||
|
writeConf(data) |
||||||
|
|
||||||
|
db = mw.getMyORM() |
||||||
|
|
||||||
|
db.setHost(args['db_host']) |
||||||
|
db.setPort(args['db_port']) |
||||||
|
db.setUser(args['db_user']) |
||||||
|
db.setPwd(args['db_pass']) |
||||||
|
testdata = db.query('select version()') |
||||||
|
|
||||||
|
isError = isSqlError(testdata) |
||||||
|
if isError != None: |
||||||
|
return isError |
||||||
|
|
||||||
|
return mw.returnJson(True, '保存成功,并连通成功!', []) |
||||||
|
|
||||||
|
|
||||||
|
def getUserConf(): |
||||||
|
data = getConfigData() |
||||||
|
if 'user' in data: |
||||||
|
return mw.returnJson(True, 'ok', data['user']) |
||||||
|
return mw.returnJson(False, 'ok', {}) |
||||||
|
|
||||||
|
|
||||||
|
def getUserConf(): |
||||||
|
data = getConfigData() |
||||||
|
if 'user' in data: |
||||||
|
try: |
||||||
|
udata = mw.deDoubleCrypt('mw', data['user']) |
||||||
|
udata = json.loads(udata) |
||||||
|
|
||||||
|
return mw.returnJson(True, 'ok', udata) |
||||||
|
except Exception as e: |
||||||
|
pass |
||||||
|
return mw.returnJson(False, 'ok', {}) |
||||||
|
|
||||||
|
|
||||||
|
def setUserConf(): |
||||||
|
args = getArgs() |
||||||
|
data_args = checkArgs(args, ['app_key', 'secret', |
||||||
|
'password', 'uid', 'exchange']) |
||||||
|
if not data_args[0]: |
||||||
|
return data_args[1] |
||||||
|
|
||||||
|
data = getConfigData() |
||||||
|
data['user'] = mw.enDoubleCrypt('mw', json.dumps(args)) |
||||||
|
writeConf(data) |
||||||
|
|
||||||
|
return mw.returnJson(True, '保存成功!', []) |
||||||
|
|
||||||
|
|
||||||
|
def syncDataList(): |
||||||
|
data = getConfigData() |
||||||
|
|
||||||
|
name = "ct_task" |
||||||
|
if 'token' in data: |
||||||
|
|
||||||
|
rdata = {} |
||||||
|
rdata['task_status'] = False |
||||||
|
sup_path = mw.getServerDir() + '/supervisor' |
||||||
|
sup_task_dst = sup_path + '/conf.d/' + name + '.ini' |
||||||
|
if os.path.exists(sup_task_dst): |
||||||
|
rdata['task_status'] = True |
||||||
|
|
||||||
|
rdata['list'] = data['token'] |
||||||
|
return mw.returnJson(True, 'ok', rdata) |
||||||
|
return mw.returnJson(False, 'ok') |
||||||
|
|
||||||
|
|
||||||
|
def syncDataAdd(): |
||||||
|
args = getArgs() |
||||||
|
data_args = checkArgs(args, ['token']) |
||||||
|
if not data_args[0]: |
||||||
|
return data_args[1] |
||||||
|
|
||||||
|
add_token = args['token'] |
||||||
|
data = getConfigData() |
||||||
|
|
||||||
|
if 'token' in data and data['token']: |
||||||
|
if not add_token in data['token']: |
||||||
|
data['token'].append(add_token) |
||||||
|
else: |
||||||
|
data['token'] = [add_token] |
||||||
|
writeConf(data) |
||||||
|
|
||||||
|
return mw.returnJson(True, '保存成功!') |
||||||
|
|
||||||
|
|
||||||
|
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, '删除成功!') |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
func = sys.argv[1] |
||||||
|
if func == 'status': |
||||||
|
print(status()) |
||||||
|
elif func == 'start': |
||||||
|
print(start()) |
||||||
|
elif func == 'stop': |
||||||
|
print(stop()) |
||||||
|
elif func == 'restart': |
||||||
|
print(op()) |
||||||
|
elif func == 'reload': |
||||||
|
print(op()) |
||||||
|
elif func == 'get_db_conf': |
||||||
|
print(getDbConf()) |
||||||
|
elif func == 'set_db_conf': |
||||||
|
print(setDbConf()) |
||||||
|
elif func == 'get_user_conf': |
||||||
|
print(getUserConf()) |
||||||
|
elif func == 'set_user_conf': |
||||||
|
print(setUserConf()) |
||||||
|
elif func == 'sync_data_list': |
||||||
|
print(syncDataList()) |
||||||
|
elif func == 'sync_data_add': |
||||||
|
print(syncDataAdd()) |
||||||
|
elif func == 'sync_data_delete': |
||||||
|
print(syncDataDelete()) |
||||||
|
elif func == 'sync_data_add_task': |
||||||
|
print(syncDataAddTask()) |
||||||
|
else: |
||||||
|
print('error') |
@ -0,0 +1,36 @@ |
|||||||
|
{ |
||||||
|
"hook":[ |
||||||
|
{ |
||||||
|
"tag":"menu", |
||||||
|
"menu": { |
||||||
|
"title":"量化交易", |
||||||
|
"name":"cryptocurrency_trade", |
||||||
|
"path":"static/html/index.html", |
||||||
|
"css_path":"static/css/cryptocurrency_trade.css", |
||||||
|
"js_path":"static/js/cryptocurrency_trade.js" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"tag":"global_static", |
||||||
|
"global_static": { |
||||||
|
"title":"量化交易", |
||||||
|
"name":"cryptocurrency_trade", |
||||||
|
"css_path":"static/css/ico.css" |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"ps": "基于CCXT的数字货币量化交易插件", |
||||||
|
"name": "cryptocurrency_trade", |
||||||
|
"title": "量化交易", |
||||||
|
"versions": ["1.0"], |
||||||
|
"tip": "soft", |
||||||
|
"checks": "server/cryptocurrency_trade", |
||||||
|
"path":"server/cryptocurrency_trade", |
||||||
|
"author": "midoks", |
||||||
|
"date": "2022-02-25", |
||||||
|
"home": "https://github.com/ccxt/ccxt", |
||||||
|
"type": "量化交易", |
||||||
|
"shell": "install.sh", |
||||||
|
"pid": "5", |
||||||
|
"sort": 7 |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
#!/bin/bash |
||||||
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin |
||||||
|
export PATH |
||||||
|
|
||||||
|
# cd /www/server/mdserver-web/plugins/cryptocurrency_trade && bash install.sh install |
||||||
|
|
||||||
|
curPath=`pwd` |
||||||
|
rootPath=$(dirname "$curPath") |
||||||
|
rootPath=$(dirname "$rootPath") |
||||||
|
serverPath=$(dirname "$rootPath") |
||||||
|
|
||||||
|
|
||||||
|
VERSION=$2 |
||||||
|
|
||||||
|
# pip3 install ccxt |
||||||
|
if [ -f ${rootPath}/bin/activate ];then |
||||||
|
source ${rootPath}/bin/activate |
||||||
|
fi |
||||||
|
pip3 install ccxt |
||||||
|
pip3 install pandas |
||||||
|
pip3 install pandas_ta |
||||||
|
pip3 install pyTelegramBotAPI |
||||||
|
pip3 install catalyst |
||||||
|
pip3 install matplotlib==3.2.2 |
||||||
|
|
||||||
|
|
||||||
|
Install_App() |
||||||
|
{ |
||||||
|
echo '正在安装脚本文件...' |
||||||
|
mkdir -p $serverPath/source/cryptocurrency_trade |
||||||
|
mkdir -p $serverPath/cryptocurrency_trade |
||||||
|
echo "${VERSION}" > $serverPath/cryptocurrency_trade/version.pl |
||||||
|
|
||||||
|
if [ ! -f $serverPath/source/cryptocurrency_trade/ta-lib-0.4.0-src.tar.gz ];then |
||||||
|
wget -O $serverPath/source/cryptocurrency_trade/ta-lib-0.4.0-src.tar.gz https://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -d $serverPath/source/cryptocurrency_trade/ta-lib ];then |
||||||
|
cd $serverPath/source/cryptocurrency_trade/ta-lib && tar -xzf ta-lib-0.4.0-src.tar.gz |
||||||
|
cd ta-lib && ./configure --prefix=/usr && make && make install |
||||||
|
rm -rf $serverPath/source/cryptocurrency_trade/ta-lib |
||||||
|
|
||||||
|
pip3 install ta-lib |
||||||
|
fi |
||||||
|
|
||||||
|
cd ${rootPath} && python3 ${rootPath}/plugins/cryptocurrency_trade/index.py start |
||||||
|
echo '安装完成' |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Uninstall_App() |
||||||
|
{ |
||||||
|
rm -rf $serverPath/cryptocurrency_trade |
||||||
|
cd ${rootPath} && python3 ${rootPath}/plugins/cryptocurrency_trade/index.py stop |
||||||
|
echo "卸载完成" |
||||||
|
} |
||||||
|
|
||||||
|
action=$1 |
||||||
|
if [ "${1}" == 'install' ];then |
||||||
|
Install_App |
||||||
|
else |
||||||
|
Uninstall_App |
||||||
|
fi |
@ -0,0 +1,11 @@ |
|||||||
|
.screen-all { |
||||||
|
padding: 5px; |
||||||
|
} |
||||||
|
|
||||||
|
.s-right .panel-default{ |
||||||
|
margin-bottom: 10px; |
||||||
|
} |
||||||
|
|
||||||
|
.s-left .panel-default{ |
||||||
|
margin-bottom: 10px; |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
|
||||||
|
/* menu start */ |
||||||
|
.menu .current .menu_plugin_cryptocurrency_trade:hover { |
||||||
|
background-image: url("/plugins/file?name=cryptocurrency_trade&f=ico.png"); |
||||||
|
} |
||||||
|
|
||||||
|
.menu .menu_plugin_cryptocurrency_trade { |
||||||
|
background-image: url("/plugins/file?name=cryptocurrency_trade&f=ico.png"); |
||||||
|
} |
||||||
|
|
||||||
|
/* menu end */ |
@ -0,0 +1,65 @@ |
|||||||
|
|
||||||
|
<div class="main-content"> |
||||||
|
<div class="screen-all"> |
||||||
|
<div class="content-screen safe bgw mtb15 ptb10 radius4 row" style="height: 300px;"> |
||||||
|
<!-- start --> |
||||||
|
|
||||||
|
<div class="s-left col-md-8" style="padding-left: 2.5px;padding-right: 5px; overflow-y: scroll;height: 400px;"> |
||||||
|
|
||||||
|
<div class="panel panel-default"> |
||||||
|
<div class="panel-heading">订单数据</div> |
||||||
|
<div class="panel-body">Panel content</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="panel panel-default"> |
||||||
|
<div class="panel-heading">交易图表</div> |
||||||
|
<div class="panel-body" style="padding: 0px;"> |
||||||
|
<div></div> |
||||||
|
<div id="k_echarts" style="width: auto;height:400px;"></div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="panel panel-default"> |
||||||
|
<div class="panel-heading">策略交易日志</div> |
||||||
|
<div class="panel-body" style="padding: 0px;"> |
||||||
|
<pre id="strategy_log" style="border:none;font-size:12px;white-space: pre;margin: 0px;width: auto;height: 200px;background-color: #333;color:#fff; padding:0 5px">当前没有日志.</pre> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="s-right col-md-4" style="padding-left: 2.5px;padding-right: 5px; overflow-y: scroll;height: 400px;"> |
||||||
|
|
||||||
|
<div class="panel panel-default"> |
||||||
|
<div class="panel-heading">策略配置</div> |
||||||
|
<div class="panel-body divtable pd15 relative" style="padding: 2px 2px 5px;"> |
||||||
|
<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th width="20">序列</th> |
||||||
|
<th width="120">说明</th> |
||||||
|
<th width="10">状态</th> |
||||||
|
<th style="text-align: right;" width="50">操作</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody id="strategy_list"></tbody> |
||||||
|
</table> |
||||||
|
<div class="dataTables_paginate paging_bootstrap pagination"> |
||||||
|
<ul id="strategy_list_page" class="page"></ul> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="panel panel-default"> |
||||||
|
<div class="panel-heading">数据源日志</div> |
||||||
|
<div class="panel-body" style="padding: 0px;"> |
||||||
|
<pre id="datasource_log" style="border:none;font-size:12px;white-space: pre;margin: 0px;width: auto;height: 200px;background-color: #333;color:#fff; padding:0 5px">当前没有日志.</pre> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
<!-- end --> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
@ -0,0 +1,964 @@ |
|||||||
|
|
||||||
|
function appPost(method,args,callback, title){ |
||||||
|
var _args = null;
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
_args = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
_args = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
var _title = '正在获取...'; |
||||||
|
if (typeof(title) != 'undefined'){ |
||||||
|
_title = title; |
||||||
|
} |
||||||
|
|
||||||
|
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 }); |
||||||
|
$.post('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args}, function(data) { |
||||||
|
layer.close(loadT); |
||||||
|
if (!data.status){ |
||||||
|
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof(callback) == 'function'){ |
||||||
|
callback(data); |
||||||
|
} |
||||||
|
},'json');
|
||||||
|
} |
||||||
|
|
||||||
|
function appPostN(method,args,callback, title){ |
||||||
|
|
||||||
|
var _args = null;
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
_args = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
_args = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
var _title = '正在获取...'; |
||||||
|
if (typeof(title) != 'undefined'){ |
||||||
|
_title = title; |
||||||
|
} |
||||||
|
$.post('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args}, function(data) { |
||||||
|
if(typeof(callback) == 'function'){ |
||||||
|
callback(data); |
||||||
|
} |
||||||
|
},'json');
|
||||||
|
} |
||||||
|
|
||||||
|
function appAsyncPost(method,args){ |
||||||
|
var _args = null;
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
_args = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
_args = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); |
||||||
|
return syncPost('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function appPostCallbak(method, args,callback, script){ |
||||||
|
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); |
||||||
|
|
||||||
|
var req_data = {}; |
||||||
|
req_data['name'] = 'cryptocurrency_trade'; |
||||||
|
req_data['func'] = method; |
||||||
|
|
||||||
|
if (typeof(script) != 'undefined'){ |
||||||
|
req_data['script'] = script; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
req_data['args'] = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
req_data['args'] = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
$.post('/plugins/callback', req_data, function(data) { |
||||||
|
layer.close(loadT); |
||||||
|
if (!data.status){ |
||||||
|
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof(callback) == 'function'){ |
||||||
|
callback(data); |
||||||
|
} |
||||||
|
},'json');
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function appPostCallbakNoMsg(method, args,callback, script){ |
||||||
|
|
||||||
|
var req_data = {}; |
||||||
|
req_data['name'] = 'cryptocurrency_trade'; |
||||||
|
req_data['func'] = method; |
||||||
|
|
||||||
|
if (typeof(script) != 'undefined'){ |
||||||
|
req_data['script'] = script; |
||||||
|
} else { |
||||||
|
req_data['script'] = req_data['name']; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (typeof(args) == 'string'){ |
||||||
|
req_data['args'] = JSON.stringify(toArrayObject(args)); |
||||||
|
} else { |
||||||
|
req_data['args'] = JSON.stringify(args); |
||||||
|
} |
||||||
|
|
||||||
|
$.post('/plugins/callback', req_data, function(data) { |
||||||
|
if (!data.status){ |
||||||
|
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if(typeof(callback) == 'function'){ |
||||||
|
callback(data); |
||||||
|
} |
||||||
|
},'json');
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function dbConf(){ |
||||||
|
appPost('get_db_conf','',function(data){ |
||||||
|
var rdata = $.parseJSON(data.data); |
||||||
|
// console.log(rdata);
|
||||||
|
var db_host = '127.0.0.1'; |
||||||
|
var db_port = '3306'; |
||||||
|
var db_name = 'cryptocurrency_trade'; |
||||||
|
var db_user = 'cryptocurrency_trade'; |
||||||
|
var db_pass = 'cryptocurrency_trade'; |
||||||
|
if(rdata['status']){ |
||||||
|
db_data = rdata['data']; |
||||||
|
db_host = db_data['db_host']; |
||||||
|
db_port = db_data['db_port']; |
||||||
|
db_name = db_data['db_name']; |
||||||
|
db_user = db_data['db_user']; |
||||||
|
db_pass = db_data['db_pass']; |
||||||
|
} |
||||||
|
|
||||||
|
var mlist = ''; |
||||||
|
mlist += '<p><span>数据库地址</span><input style="width: 250px;" class="bt-input-text mr5" name="db_host" value="'+db_host+'" type="text"></p>' |
||||||
|
mlist += '<p><span>数据库端口</span><input style="width: 250px;" class="bt-input-text mr5" name="db_port" value="'+db_port+'" type="text"></p>' |
||||||
|
mlist += '<p><span>数据库名称</span><input style="width: 250px;" class="bt-input-text mr5" name="db_name" value="'+db_name+'" type="text"></p>' |
||||||
|
mlist += '<p><span>用户名</span><input style="width: 250px;" class="bt-input-text mr5" name="db_user" value="'+db_user+'" type="text"></p>' |
||||||
|
mlist += '<p><span>密码</span><input style="width: 250px;" class="bt-input-text mr5" name="db_pass" value="'+db_pass+'" type="text"></p>' |
||||||
|
|
||||||
|
var option = '<style>.conf_p p{margin-bottom: 2px}</style>\ |
||||||
|
<div class="conf_p" style="margin-bottom:0">\ |
||||||
|
' + mlist + '\ |
||||||
|
<div style="margin-top:10px; padding-right:15px" class="text-right">\ |
||||||
|
<button class="btn btn-success btn-sm" onclick="submitDbConf()">保存</button>\ |
||||||
|
</div>\ |
||||||
|
</div>'; |
||||||
|
$(".soft-man-con").html(option); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function submitDbConf(){ |
||||||
|
|
||||||
|
var pull_data = {}; |
||||||
|
|
||||||
|
pull_data['db_host'] = $('input[name="db_host"]').val(); |
||||||
|
pull_data['db_port'] = $('input[name="db_port"]').val(); |
||||||
|
pull_data['db_name'] = $('input[name="db_name"]').val(); |
||||||
|
pull_data['db_user'] = $('input[name="db_user"]').val(); |
||||||
|
pull_data['db_pass'] = $('input[name="db_pass"]').val(); |
||||||
|
|
||||||
|
appPost('set_db_conf',pull_data,function(data){ |
||||||
|
var rdata = $.parseJSON(data.data); |
||||||
|
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function userConf(){ |
||||||
|
appPost('get_user_conf','',function(data){ |
||||||
|
var rdata = $.parseJSON(data.data); |
||||||
|
var app_key = 'app_key'; |
||||||
|
var secret = 'secret'; |
||||||
|
var password = 'password'; |
||||||
|
var uid = 'uid'; |
||||||
|
var exchange = 'okex'; |
||||||
|
if(rdata['status']){ |
||||||
|
db_data = rdata['data']; |
||||||
|
app_key = db_data['app_key']; |
||||||
|
secret = db_data['secret']; |
||||||
|
password = db_data['password']; |
||||||
|
uid = db_data['uid']; |
||||||
|
exchange = db_data['exchange'];; |
||||||
|
} |
||||||
|
|
||||||
|
var mlist = ''; |
||||||
|
mlist += '<p><span>交易所</span><input style="width: 250px;" class="bt-input-text mr5" name="exchange" value="'+exchange+'" type="text"><font>必填写[okex, binance]</font></p>' |
||||||
|
mlist += '<p><span>apiKey</span><input style="width: 250px;" class="bt-input-text mr5" name="app_key" value="'+app_key+'" type="text"><font>必填写</font></p>' |
||||||
|
mlist += '<p><span>secret</span><input style="width: 250px;" class="bt-input-text mr5" name="secret" value="'+secret+'" type="text"><font>必填写</font></p>' |
||||||
|
mlist += '<p><span>password</span><input style="width: 250px;" class="bt-input-text mr5" name="password" value="'+password+'" type="text"><font>根据情况填写</font></p>' |
||||||
|
mlist += '<p><span>uid</span><input style="width: 250px;" class="bt-input-text mr5" name="uid" value="'+uid+'" type="text"><font>根据情况填写</font></p>' |
||||||
|
|
||||||
|
var option = '<style>.conf_p p{margin-bottom: 2px}</style>\ |
||||||
|
<div class="conf_p" style="margin-bottom:0">\ |
||||||
|
' + mlist + '\ |
||||||
|
<div style="margin-top:10px; padding-right:15px" class="text-right">\ |
||||||
|
<button class="btn btn-success btn-sm" onclick="submitUserConf()">保存</button>\ |
||||||
|
</div>\ |
||||||
|
</div>'; |
||||||
|
$(".soft-man-con").html(option); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function submitUserConf(){ |
||||||
|
var pull_data = {}; |
||||||
|
|
||||||
|
pull_data['app_key'] = $('input[name="app_key"]').val(); |
||||||
|
pull_data['secret'] = $('input[name="secret"]').val(); |
||||||
|
pull_data['password'] = $('input[name="password"]').val(); |
||||||
|
pull_data['uid'] = $('input[name="uid"]').val(); |
||||||
|
pull_data['exchange'] = $('input[name="exchange"]').val(); |
||||||
|
|
||||||
|
appPost('set_user_conf',pull_data,function(data){ |
||||||
|
var rdata = $.parseJSON(data.data); |
||||||
|
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function syncDataList(){ |
||||||
|
appPost('sync_data_list', {}, function(data){ |
||||||
|
var rdata = $.parseJSON(data.data); |
||||||
|
|
||||||
|
|
||||||
|
var list = ''; |
||||||
|
if (rdata['status']){ |
||||||
|
var dlist = rdata['data']['list']; |
||||||
|
for(i in dlist){ |
||||||
|
|
||||||
|
list += '<tr>'; |
||||||
|
list += '<td>' + dlist[i] +'</td>'; |
||||||
|
list += '<td style="text-align:right">' + |
||||||
|
'<a href="javascript:;" class="btlink" onclick="syncDataDelete(\''+dlist[i]+'\')" title="删除">删除</a>' + |
||||||
|
'</td>'; |
||||||
|
list += '</tr>'; |
||||||
|
}
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if( list == '' ){ |
||||||
|
list = "<tr><td colspan='2'>当前没有数据</td></tr>"; |
||||||
|
} |
||||||
|
|
||||||
|
var task_status = rdata['data']['task_status']; |
||||||
|
var task_status_check = ''; |
||||||
|
if (task_status){ |
||||||
|
task_status_check = 'checked'; |
||||||
|
} |
||||||
|
|
||||||
|
var con = '<div class="safe bgw">\ |
||||||
|
<div>\ |
||||||
|
<button onclick="syncDataAdd()" title="添加币种" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加币种</button>\ |
||||||
|
<div class="ss-text pull-left mr50">\ |
||||||
|
<em>是否启动</em>\ |
||||||
|
<div class="ssh-item">\ |
||||||
|
<input class="btswitch btswitch-ios" id="add_task" type="checkbox" '+task_status_check+'>\ |
||||||
|
<label class="btswitch-btn" for="add_task" onclick="syncDataAddTask()"></label>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<div class="divtable mtb10">\ |
||||||
|
<div class="tablescroll">\ |
||||||
|
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\ |
||||||
|
<thead>\ |
||||||
|
<th>名称</th>\ |
||||||
|
<th style="text-align:right;">操作</th></tr>\ |
||||||
|
</thead>\ |
||||||
|
<tbody>'+ list +'</tbody>\ |
||||||
|
</table>\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
</div>'; |
||||||
|
|
||||||
|
con += '<div class="code">\ |
||||||
|
<span>详细如下:</span>\ |
||||||
|
<span>*:添加同步的币种,都小写,以USDT为本币同步数据。</span>\ |
||||||
|
<span>*:需要提前安装supervisor插件。</span>\ |
||||||
|
</div>' |
||||||
|
|
||||||
|
$(".soft-man-con").html(con); |
||||||
|
$('#databasePage').html(rdata.page); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function syncDataAddTask(){ |
||||||
|
var at_check = $('#add_task').prop('checked'); |
||||||
|
appPost("sync_data_add_task", {'check':at_check?'0':'1'}, function(data){ |
||||||
|
rdata = $.parseJSON(data.data); |
||||||
|
|
||||||
|
showMsg(rdata.msg,function(){ |
||||||
|
if (rdata.status){ |
||||||
|
syncDataList(); |
||||||
|
} |
||||||
|
},{icon:rdata.status?1:2}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function syncDataDelete(name){ |
||||||
|
appPost("sync_data_delete", {"token":name}, function(data){ |
||||||
|
rdata = $.parseJSON(data.data); |
||||||
|
|
||||||
|
showMsg(rdata.msg,function(){ |
||||||
|
if (rdata.status){ |
||||||
|
syncDataList(); |
||||||
|
} |
||||||
|
},{icon:rdata.status?1:2}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function syncDataAdd() { |
||||||
|
layer.open({ |
||||||
|
type: 1, |
||||||
|
area: '500px', |
||||||
|
title: '添加同步数据', |
||||||
|
closeBtn: 2, |
||||||
|
shift: 0, |
||||||
|
shadeClose: false, |
||||||
|
btn: ['确定', '取消'], |
||||||
|
content: "<div class='bt_conter bt-form pd15' style='height:auto;width:100%;'>\ |
||||||
|
<div class='line'>\ |
||||||
|
<span class='tname'>名称</span>\ |
||||||
|
<div class='info-r c4'>\ |
||||||
|
<input id='name' class='bt-input-text' type='text' name='name' placeholder='请输入名称' style='width:270px' />\ |
||||||
|
</div>\ |
||||||
|
</div>\ |
||||||
|
<ul class='help-info-text c7' style='padding-left: 29px;margin-top:5px;'>\ |
||||||
|
<li style='color:#F00'>注意:币种名称用小写!</li>\ |
||||||
|
</ul>\ |
||||||
|
</div>", |
||||||
|
yes: function(index, layero){ |
||||||
|
|
||||||
|
var token = $('input[name="name"]').val(); |
||||||
|
appPost("sync_data_add", {"token":token}, function(data){ |
||||||
|
rdata = $.parseJSON(data.data); |
||||||
|
showMsg(rdata.msg,function(){ |
||||||
|
if (rdata.status){ |
||||||
|
layer.close(index); |
||||||
|
syncDataList(); |
||||||
|
} |
||||||
|
},{icon:rdata.status?1:2}); |
||||||
|
}) |
||||||
|
return; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function onlineEditStrategyFile(k, f, tag) { |
||||||
|
if(k != 0) { |
||||||
|
var l = $("#PathPlace input").val(); |
||||||
|
var h = $("#textBody").val(); |
||||||
|
var a = $("select[name=encoding]").val(); |
||||||
|
var loadT = layer.msg("正在保存中...", {icon: 16,time: 0}); |
||||||
|
appPostCallbakNoMsg('save_body',{'data':h,'path':f,'encoding':a,"tag":tag}, function(data){ |
||||||
|
var rdata = data.data; |
||||||
|
showMsg(rdata.msg, function(){ |
||||||
|
if (rdata.status){ |
||||||
|
layer.close(loadT); |
||||||
|
} |
||||||
|
},{icon: rdata.status ? 1 : 2}); |
||||||
|
}); |
||||||
|
return |
||||||
|
} |
||||||
|
var e = layer.msg("正在读取文件,请稍候...", {icon: 16,time: 0}); |
||||||
|
var g = f.split("."); |
||||||
|
var b = g[g.length - 1]; |
||||||
|
var d; |
||||||
|
switch(b) { |
||||||
|
case "html": |
||||||
|
var j = { |
||||||
|
name: "htmlmixed", |
||||||
|
scriptTypes: [{ |
||||||
|
matches: /\/x-handlebars-template|\/x-mustache/i, |
||||||
|
mode: null |
||||||
|
}, { |
||||||
|
matches: /(text|application)\/(x-)?vb(a|script)/i, |
||||||
|
mode: "vbscript" |
||||||
|
}] |
||||||
|
}; |
||||||
|
d = j; |
||||||
|
break; |
||||||
|
case "htm": |
||||||
|
var j = { |
||||||
|
name: "htmlmixed", |
||||||
|
scriptTypes: [{ |
||||||
|
matches: /\/x-handlebars-template|\/x-mustache/i, |
||||||
|
mode: null |
||||||
|
}, { |
||||||
|
matches: /(text|application)\/(x-)?vb(a|script)/i, |
||||||
|
mode: "vbscript" |
||||||
|
}] |
||||||
|
}; |
||||||
|
d = j; |
||||||
|
break; |
||||||
|
case "js": |
||||||
|
d = "text/javascript"; |
||||||
|
break; |
||||||
|
case "json": |
||||||
|
d = "application/ld+json"; |
||||||
|
break; |
||||||
|
case "css": |
||||||
|
d = "text/css"; |
||||||
|
break; |
||||||
|
case "php": |
||||||
|
d = "application/x-httpd-php"; |
||||||
|
break; |
||||||
|
case "tpl": |
||||||
|
d = "application/x-httpd-php"; |
||||||
|
break; |
||||||
|
case "xml": |
||||||
|
d = "application/xml"; |
||||||
|
break; |
||||||
|
case "sql": |
||||||
|
d = "text/x-sql"; |
||||||
|
break; |
||||||
|
case "conf": |
||||||
|
d = "text/x-nginx-conf"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
var j = { |
||||||
|
name: "htmlmixed", |
||||||
|
scriptTypes: [{ |
||||||
|
matches: /\/x-handlebars-template|\/x-mustache/i, |
||||||
|
mode: null |
||||||
|
}, { |
||||||
|
matches: /(text|application)\/(x-)?vb(a|script)/i, |
||||||
|
mode: "vbscript" |
||||||
|
}] |
||||||
|
}; |
||||||
|
d = j |
||||||
|
} |
||||||
|
$.post("/files/get_body", "path=" + encodeURIComponent(f), function(s) { |
||||||
|
if(s.status === false){ |
||||||
|
layer.msg(s.msg,{icon:5}); |
||||||
|
return; |
||||||
|
} |
||||||
|
layer.close(e); |
||||||
|
var u = ["utf-8", "GBK", "GB2312", "BIG5"]; |
||||||
|
var n = ""; |
||||||
|
var m = ""; |
||||||
|
var o = ""; |
||||||
|
for(var p = 0; p < u.length; p++) { |
||||||
|
m = s.data.encoding == u[p] ? "selected" : ""; |
||||||
|
n += '<option value="' + u[p] + '" ' + m + ">" + u[p] + "</option>"; |
||||||
|
} |
||||||
|
var code_mirror = null;
|
||||||
|
var r = layer.open({ |
||||||
|
type: 1, |
||||||
|
shift: 5, |
||||||
|
closeBtn: 1, |
||||||
|
area: ["90%", "90%"], |
||||||
|
title: "在线编辑[" + f + "]", |
||||||
|
btn:['保存','关闭'], |
||||||
|
content: '<form class="bt-form pd20">\ |
||||||
|
<div class="line">\ |
||||||
|
<p style="color:red;margin-bottom:10px">提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!\ |
||||||
|
<select class="bt-input-text" name="encoding" style="width: 74px;position: absolute;top: 31px;right: 19px;height: 22px;z-index: 9999;border-radius: 0;">' + n + '</select>\ |
||||||
|
</p>\ |
||||||
|
<textarea class="mCustomScrollbar bt-input-text" id="textBody" style="width:100%;margin:0 auto;line-height: 1.8;position: relative;top: 10px;" value="" />\ |
||||||
|
</div>\ |
||||||
|
</form>', |
||||||
|
success:function(){ |
||||||
|
$("#textBody").text(s.data.data); |
||||||
|
var q = $(window).height() * 0.9; |
||||||
|
$("#textBody").height(q - 160); |
||||||
|
code_mirror = CodeMirror.fromTextArea(document.getElementById("textBody"), { |
||||||
|
extraKeys: { |
||||||
|
"Ctrl-F": "findPersistent", |
||||||
|
"Ctrl-H": "replaceAll", |
||||||
|
"Ctrl-S": function() { |
||||||
|
$("#textBody").text(code_mirror.getValue()); |
||||||
|
onlineEditStrategyFile(2, f,tag); |
||||||
|
}, |
||||||
|
"Cmd-S":function() { |
||||||
|
$("#textBody").text(code_mirror.getValue()); |
||||||
|
onlineEditStrategyFile(2, f,tag); |
||||||
|
}, |
||||||
|
}, |
||||||
|
mode: d, |
||||||
|
lineNumbers: true, |
||||||
|
matchBrackets: true, |
||||||
|
matchtags: true, |
||||||
|
autoMatchParens: true |
||||||
|
}); |
||||||
|
code_mirror.focus(); |
||||||
|
code_mirror.setSize("auto", q - 150); |
||||||
|
$(window).resize(function(){ |
||||||
|
var q = $(window).height() * 0.9; |
||||||
|
code_mirror.setSize("auto", q - 150); |
||||||
|
});
|
||||||
|
}, |
||||||
|
yes:function(){ |
||||||
|
$("#textBody").text(code_mirror.getValue()); |
||||||
|
onlineEditStrategyFile(1, f, tag); |
||||||
|
} |
||||||
|
}); |
||||||
|
//////////////////
|
||||||
|
},'json'); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// 大屏页功能 --------------------------------------------------------------------
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
function changeDivH(){ |
||||||
|
var l = $(window).height(); |
||||||
|
$('.content-screen').css('height',l-80); |
||||||
|
$('.s-right').css('height',l-80-10); |
||||||
|
$('.s-left').css('height',l-80-10); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function dataSourceLog(){ |
||||||
|
appPostCallbakNoMsg('get_datasource_logs',{}, function(rdata){ |
||||||
|
$('#datasource_log').html(rdata.data); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function dataStrategyLog(){ |
||||||
|
appPostCallbakNoMsg('get_strategy_logs',{}, function(rdata){ |
||||||
|
$('#strategy_log').html(rdata.data); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// $('#strategy_list').find('span[data-id="01"]')
|
||||||
|
function setStrategyStatus(id,status){ |
||||||
|
appPostCallbakNoMsg('set_strategy_status',{"id":id,"status":status}, function(data){ |
||||||
|
var rdata = data.data; |
||||||
|
showMsg(rdata.msg,function(){ |
||||||
|
if (rdata.status){ |
||||||
|
if (status == 'start'){ |
||||||
|
$('#strategy_list').find('tr[data-id="'+id+'"] td span').removeClass('glyphicon-pause').addClass('glyphicon-play').css('color','#20a53a'); |
||||||
|
} else{ |
||||||
|
$('#strategy_list').find('tr[data-id="'+id+'"] td span').removeClass('glyphicon-play').addClass('glyphicon-pause').css('color','red'); |
||||||
|
} |
||||||
|
} |
||||||
|
},{icon:rdata.status?1:2},2000); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function setStrategyRestart(id){ |
||||||
|
appPostCallbakNoMsg('set_strategy_restart',{"id":id}, function(data){ |
||||||
|
console.log(data); |
||||||
|
var rdata = data.data; |
||||||
|
showMsg(rdata.msg,function(){ |
||||||
|
if (rdata.status){ |
||||||
|
} |
||||||
|
},{icon:rdata.status?1:2},2000); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function setStrategyEdit(id){ |
||||||
|
appPostCallbakNoMsg('get_strategy_path',{"id":id}, function(data){ |
||||||
|
onlineEditStrategyFile(0,data.data.msg,id); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function getStrategyList(p=1){ |
||||||
|
appPostCallbakNoMsg('get_strategy_list',{'page':p}, function(rdata){ |
||||||
|
// console.log(rdata);
|
||||||
|
|
||||||
|
ldata = rdata.data.data; |
||||||
|
var tBody = ''; |
||||||
|
|
||||||
|
for (var i = 0; i < ldata.length; i++) { |
||||||
|
tBody += '<tr data-id="'+ldata[i]['id']+'">' |
||||||
|
tBody += '<td>'+ldata[i]['id']+'</td>'; |
||||||
|
tBody += '<td>'+ldata[i]['name']+'</td>'; |
||||||
|
|
||||||
|
if (ldata[i]['status'] == 'start'){ |
||||||
|
tBody += '<td><span style="color:#20a53a;cursor: pointer;" class="strategy_status glyphicon glyphicon-play"></span></td>'; |
||||||
|
} else{ |
||||||
|
tBody += '<td><span style="color:red;cursor: pointer;" class="strategy_status glyphicon glyphicon-pause"></span></td>'; |
||||||
|
} |
||||||
|
|
||||||
|
tBody += "<td style='text-align: right;'><a class='btlink restart'>重启</a> | <a class='btlink edit'>编辑</a></td>"; |
||||||
|
tBody +='<tr>'; |
||||||
|
} |
||||||
|
|
||||||
|
// console.log(tBody);
|
||||||
|
$('#strategy_list').html(tBody); |
||||||
|
$('#strategy_list_page').html(rdata.data.list); |
||||||
|
|
||||||
|
|
||||||
|
$('#strategy_list .strategy_status').click(function(){ |
||||||
|
var id = $(this).parent().parent().data('id'); |
||||||
|
var status = 'stop'; |
||||||
|
if ($(this).hasClass('glyphicon-pause')){ |
||||||
|
status = 'start'; |
||||||
|
} |
||||||
|
setStrategyStatus(id,status); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#strategy_list .restart').click(function(){ |
||||||
|
var id = $(this).parent().parent().data('id'); |
||||||
|
setStrategyRestart(id); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#strategy_list .edit').click(function(){ |
||||||
|
var id = $(this).parent().parent().data('id'); |
||||||
|
setStrategyEdit(id); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function calcKLineChats(){ |
||||||
|
|
||||||
|
var chartDom = document.getElementById('k_echarts'); |
||||||
|
var myChart = echarts.init(chartDom); |
||||||
|
var option; |
||||||
|
|
||||||
|
const upColor = '#ec0000'; |
||||||
|
const upBorderColor = '#8A0000'; |
||||||
|
const downColor = '#00da3c'; |
||||||
|
const downBorderColor = '#008F28'; |
||||||
|
// Each item: open,close,lowest,highest
|
||||||
|
const data0 = splitData([ |
||||||
|
['2013/1/24', 2320.26, 2320.26, 2287.3, 2362.94], |
||||||
|
['2013/1/25', 2300, 2291.3, 2288.26, 2308.38], |
||||||
|
['2013/1/28', 2295.35, 2346.5, 2295.35, 2346.92], |
||||||
|
['2013/1/29', 2347.22, 2358.98, 2337.35, 2363.8], |
||||||
|
['2013/1/30', 2360.75, 2382.48, 2347.89, 2383.76], |
||||||
|
['2013/1/31', 2383.43, 2385.42, 2371.23, 2391.82], |
||||||
|
['2013/2/1', 2377.41, 2419.02, 2369.57, 2421.15], |
||||||
|
['2013/2/4', 2425.92, 2428.15, 2417.58, 2440.38], |
||||||
|
['2013/2/5', 2411, 2433.13, 2403.3, 2437.42], |
||||||
|
['2013/2/6', 2432.68, 2434.48, 2427.7, 2441.73], |
||||||
|
['2013/2/7', 2430.69, 2418.53, 2394.22, 2433.89], |
||||||
|
['2013/2/8', 2416.62, 2432.4, 2414.4, 2443.03], |
||||||
|
['2013/2/18', 2441.91, 2421.56, 2415.43, 2444.8], |
||||||
|
['2013/2/19', 2420.26, 2382.91, 2373.53, 2427.07], |
||||||
|
['2013/2/20', 2383.49, 2397.18, 2370.61, 2397.94], |
||||||
|
['2013/2/21', 2378.82, 2325.95, 2309.17, 2378.82], |
||||||
|
['2013/2/22', 2322.94, 2314.16, 2308.76, 2330.88], |
||||||
|
['2013/2/25', 2320.62, 2325.82, 2315.01, 2338.78], |
||||||
|
['2013/2/26', 2313.74, 2293.34, 2289.89, 2340.71], |
||||||
|
['2013/2/27', 2297.77, 2313.22, 2292.03, 2324.63], |
||||||
|
['2013/2/28', 2322.32, 2365.59, 2308.92, 2366.16], |
||||||
|
['2013/3/1', 2364.54, 2359.51, 2330.86, 2369.65], |
||||||
|
['2013/3/4', 2332.08, 2273.4, 2259.25, 2333.54], |
||||||
|
['2013/3/5', 2274.81, 2326.31, 2270.1, 2328.14], |
||||||
|
['2013/3/6', 2333.61, 2347.18, 2321.6, 2351.44], |
||||||
|
['2013/3/7', 2340.44, 2324.29, 2304.27, 2352.02], |
||||||
|
['2013/3/8', 2326.42, 2318.61, 2314.59, 2333.67], |
||||||
|
['2013/3/11', 2314.68, 2310.59, 2296.58, 2320.96], |
||||||
|
['2013/3/12', 2309.16, 2286.6, 2264.83, 2333.29], |
||||||
|
['2013/3/13', 2282.17, 2263.97, 2253.25, 2286.33], |
||||||
|
['2013/3/14', 2255.77, 2270.28, 2253.31, 2276.22], |
||||||
|
['2013/3/15', 2269.31, 2278.4, 2250, 2312.08], |
||||||
|
['2013/3/18', 2267.29, 2240.02, 2239.21, 2276.05], |
||||||
|
['2013/3/19', 2244.26, 2257.43, 2232.02, 2261.31], |
||||||
|
['2013/3/20', 2257.74, 2317.37, 2257.42, 2317.86], |
||||||
|
['2013/3/21', 2318.21, 2324.24, 2311.6, 2330.81], |
||||||
|
['2013/3/22', 2321.4, 2328.28, 2314.97, 2332], |
||||||
|
['2013/3/25', 2334.74, 2326.72, 2319.91, 2344.89], |
||||||
|
['2013/3/26', 2318.58, 2297.67, 2281.12, 2319.99], |
||||||
|
['2013/3/27', 2299.38, 2301.26, 2289, 2323.48], |
||||||
|
['2013/3/28', 2273.55, 2236.3, 2232.91, 2273.55], |
||||||
|
['2013/3/29', 2238.49, 2236.62, 2228.81, 2246.87], |
||||||
|
['2013/4/1', 2229.46, 2234.4, 2227.31, 2243.95], |
||||||
|
['2013/4/2', 2234.9, 2227.74, 2220.44, 2253.42], |
||||||
|
['2013/4/3', 2232.69, 2225.29, 2217.25, 2241.34], |
||||||
|
['2013/4/8', 2196.24, 2211.59, 2180.67, 2212.59], |
||||||
|
['2013/4/9', 2215.47, 2225.77, 2215.47, 2234.73], |
||||||
|
['2013/4/10', 2224.93, 2226.13, 2212.56, 2233.04], |
||||||
|
['2013/4/11', 2236.98, 2219.55, 2217.26, 2242.48], |
||||||
|
['2013/4/12', 2218.09, 2206.78, 2204.44, 2226.26], |
||||||
|
['2013/4/15', 2199.91, 2181.94, 2177.39, 2204.99], |
||||||
|
['2013/4/16', 2169.63, 2194.85, 2165.78, 2196.43], |
||||||
|
['2013/4/17', 2195.03, 2193.8, 2178.47, 2197.51], |
||||||
|
['2013/4/18', 2181.82, 2197.6, 2175.44, 2206.03], |
||||||
|
['2013/4/19', 2201.12, 2244.64, 2200.58, 2250.11], |
||||||
|
['2013/4/22', 2236.4, 2242.17, 2232.26, 2245.12], |
||||||
|
['2013/4/23', 2242.62, 2184.54, 2182.81, 2242.62], |
||||||
|
['2013/4/24', 2187.35, 2218.32, 2184.11, 2226.12], |
||||||
|
['2013/4/25', 2213.19, 2199.31, 2191.85, 2224.63], |
||||||
|
['2013/4/26', 2203.89, 2177.91, 2173.86, 2210.58], |
||||||
|
['2013/5/2', 2170.78, 2174.12, 2161.14, 2179.65], |
||||||
|
['2013/5/3', 2179.05, 2205.5, 2179.05, 2222.81], |
||||||
|
['2013/5/6', 2212.5, 2231.17, 2212.5, 2236.07], |
||||||
|
['2013/5/7', 2227.86, 2235.57, 2219.44, 2240.26], |
||||||
|
['2013/5/8', 2242.39, 2246.3, 2235.42, 2255.21], |
||||||
|
['2013/5/9', 2246.96, 2232.97, 2221.38, 2247.86], |
||||||
|
['2013/5/10', 2228.82, 2246.83, 2225.81, 2247.67], |
||||||
|
['2013/5/13', 2247.68, 2241.92, 2231.36, 2250.85], |
||||||
|
['2013/5/14', 2238.9, 2217.01, 2205.87, 2239.93], |
||||||
|
['2013/5/15', 2217.09, 2224.8, 2213.58, 2225.19], |
||||||
|
['2013/5/16', 2221.34, 2251.81, 2210.77, 2252.87], |
||||||
|
['2013/5/17', 2249.81, 2282.87, 2248.41, 2288.09], |
||||||
|
['2013/5/20', 2286.33, 2299.99, 2281.9, 2309.39], |
||||||
|
['2013/5/21', 2297.11, 2305.11, 2290.12, 2305.3], |
||||||
|
['2013/5/22', 2303.75, 2302.4, 2292.43, 2314.18], |
||||||
|
['2013/5/23', 2293.81, 2275.67, 2274.1, 2304.95], |
||||||
|
['2013/5/24', 2281.45, 2288.53, 2270.25, 2292.59], |
||||||
|
['2013/5/27', 2286.66, 2293.08, 2283.94, 2301.7], |
||||||
|
['2013/5/28', 2293.4, 2321.32, 2281.47, 2322.1], |
||||||
|
['2013/5/29', 2323.54, 2324.02, 2321.17, 2334.33], |
||||||
|
['2013/5/30', 2316.25, 2317.75, 2310.49, 2325.72], |
||||||
|
['2013/5/31', 2320.74, 2300.59, 2299.37, 2325.53], |
||||||
|
['2013/6/3', 2300.21, 2299.25, 2294.11, 2313.43], |
||||||
|
['2013/6/4', 2297.1, 2272.42, 2264.76, 2297.1], |
||||||
|
['2013/6/5', 2270.71, 2270.93, 2260.87, 2276.86], |
||||||
|
['2013/6/6', 2264.43, 2242.11, 2240.07, 2266.69], |
||||||
|
['2013/6/7', 2242.26, 2210.9, 2205.07, 2250.63], |
||||||
|
['2013/6/13', 2190.1, 2148.35, 2126.22, 2190.1] |
||||||
|
]); |
||||||
|
function splitData(rawData) { |
||||||
|
const categoryData = []; |
||||||
|
const values = []; |
||||||
|
for (var i = 0; i < rawData.length; i++) { |
||||||
|
categoryData.push(rawData[i].splice(0, 1)[0]); |
||||||
|
values.push(rawData[i]); |
||||||
|
} |
||||||
|
return { |
||||||
|
categoryData: categoryData, |
||||||
|
values: values |
||||||
|
}; |
||||||
|
} |
||||||
|
function calculateMA(dayCount) { |
||||||
|
var result = []; |
||||||
|
for (var i = 0, len = data0.values.length; i < len; i++) { |
||||||
|
if (i < dayCount) { |
||||||
|
result.push('-'); |
||||||
|
continue; |
||||||
|
} |
||||||
|
var sum = 0; |
||||||
|
for (var j = 0; j < dayCount; j++) { |
||||||
|
sum += +data0.values[i - j][1]; |
||||||
|
} |
||||||
|
result.push(sum / dayCount); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
option = { |
||||||
|
title: { |
||||||
|
text: '上证指数', |
||||||
|
left: 0 |
||||||
|
}, |
||||||
|
tooltip: { |
||||||
|
trigger: 'axis', |
||||||
|
axisPointer: { |
||||||
|
type: 'cross' |
||||||
|
} |
||||||
|
}, |
||||||
|
legend: { |
||||||
|
data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30'] |
||||||
|
}, |
||||||
|
grid: { |
||||||
|
left: '10%', |
||||||
|
right: '10%', |
||||||
|
bottom: '15%' |
||||||
|
}, |
||||||
|
xAxis: { |
||||||
|
type: 'category', |
||||||
|
data: data0.categoryData, |
||||||
|
boundaryGap: false, |
||||||
|
axisLine: { onZero: false }, |
||||||
|
splitLine: { show: false }, |
||||||
|
min: 'dataMin', |
||||||
|
max: 'dataMax' |
||||||
|
}, |
||||||
|
yAxis: { |
||||||
|
scale: true, |
||||||
|
splitArea: { |
||||||
|
show: true |
||||||
|
} |
||||||
|
}, |
||||||
|
dataZoom: [ |
||||||
|
{ |
||||||
|
type: 'inside', |
||||||
|
start: 50, |
||||||
|
end: 100 |
||||||
|
}, |
||||||
|
{ |
||||||
|
show: true, |
||||||
|
type: 'slider', |
||||||
|
top: '90%', |
||||||
|
start: 50, |
||||||
|
end: 100 |
||||||
|
} |
||||||
|
], |
||||||
|
series: [ |
||||||
|
{ |
||||||
|
name: '日K', |
||||||
|
type: 'candlestick', |
||||||
|
data: data0.values, |
||||||
|
itemStyle: { |
||||||
|
color: upColor, |
||||||
|
color0: downColor, |
||||||
|
borderColor: upBorderColor, |
||||||
|
borderColor0: downBorderColor |
||||||
|
}, |
||||||
|
markPoint: { |
||||||
|
label: { |
||||||
|
formatter: function (param) { |
||||||
|
return param != null ? Math.round(param.value) + '' : ''; |
||||||
|
} |
||||||
|
}, |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
name: 'Mark', |
||||||
|
coord: ['2013/5/31', 2300], |
||||||
|
value: 2300, |
||||||
|
itemStyle: { |
||||||
|
color: 'rgb(41,60,85)' |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'highest value', |
||||||
|
type: 'max', |
||||||
|
valueDim: 'highest' |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'lowest value', |
||||||
|
type: 'min', |
||||||
|
valueDim: 'lowest' |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'average value on close', |
||||||
|
type: 'average', |
||||||
|
valueDim: 'close' |
||||||
|
} |
||||||
|
], |
||||||
|
tooltip: { |
||||||
|
formatter: function (param) { |
||||||
|
return param.name + '<br>' + (param.data.coord || ''); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
markLine: { |
||||||
|
symbol: ['none', 'none'], |
||||||
|
data: [ |
||||||
|
[ |
||||||
|
{ |
||||||
|
name: 'from lowest to highest', |
||||||
|
type: 'min', |
||||||
|
valueDim: 'lowest', |
||||||
|
symbol: 'circle', |
||||||
|
symbolSize: 10, |
||||||
|
label: { |
||||||
|
show: false |
||||||
|
}, |
||||||
|
emphasis: { |
||||||
|
label: { |
||||||
|
show: false |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'max', |
||||||
|
valueDim: 'highest', |
||||||
|
symbol: 'circle', |
||||||
|
symbolSize: 10, |
||||||
|
label: { |
||||||
|
show: false |
||||||
|
}, |
||||||
|
emphasis: { |
||||||
|
label: { |
||||||
|
show: false |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
{ |
||||||
|
name: 'min line on close', |
||||||
|
type: 'min', |
||||||
|
valueDim: 'close' |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'max line on close', |
||||||
|
type: 'max', |
||||||
|
valueDim: 'close' |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'MA5', |
||||||
|
type: 'line', |
||||||
|
data: calculateMA(5), |
||||||
|
smooth: true, |
||||||
|
lineStyle: { |
||||||
|
opacity: 0.5 |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'MA10', |
||||||
|
type: 'line', |
||||||
|
data: calculateMA(10), |
||||||
|
smooth: true, |
||||||
|
lineStyle: { |
||||||
|
opacity: 0.5 |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'MA20', |
||||||
|
type: 'line', |
||||||
|
data: calculateMA(20), |
||||||
|
smooth: true, |
||||||
|
lineStyle: { |
||||||
|
opacity: 0.5 |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
name: 'MA30', |
||||||
|
type: 'line', |
||||||
|
data: calculateMA(30), |
||||||
|
smooth: true, |
||||||
|
lineStyle: { |
||||||
|
opacity: 0.5 |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
option && myChart.setOption(option); |
||||||
|
} |
||||||
|
|
||||||
|
$(document).ready(function(){ |
||||||
|
var tag = $.getUrlParam('tag'); |
||||||
|
if(tag == 'cryptocurrency_trade'){ |
||||||
|
changeDivH(); |
||||||
|
|
||||||
|
// 获取数据源更新日志
|
||||||
|
dataSourceLog(); |
||||||
|
setInterval(function(){ |
||||||
|
dataSourceLog(); |
||||||
|
},3000); |
||||||
|
|
||||||
|
// 获取策略更新日志
|
||||||
|
dataStrategyLog(); |
||||||
|
setInterval(function(){ |
||||||
|
dataStrategyLog(); |
||||||
|
},5000); |
||||||
|
|
||||||
|
getStrategyList(1); |
||||||
|
|
||||||
|
calcKLineChats(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
$(window).resize(function(){ |
||||||
|
changeDivH(); |
||||||
|
}); |
||||||
|
|
||||||
|
|
Loading…
Reference in new issue