├── .gitignore ├── accountConfig.py ├── banZhuan ├── __init__.py ├── banZhuanStrategy.py ├── fixedSpreadArbStrategy.py └── statArbStrategy.py ├── common ├── Account.py ├── Data.py ├── Errors.py ├── Log.py ├── Order.py ├── Time.py ├── UserData.py └── __init__.py ├── data └── dummy_file ├── exchangeConnection ├── __init__.py ├── bitvc │ ├── __init__.py │ ├── bitvcService.py │ ├── errors.py │ └── testBitvc.py ├── huobi │ ├── __init__.py │ ├── history.py │ ├── huobiService.py │ ├── huobiServiceETH.py │ ├── testHuobi.py │ ├── testHuobiETH.py │ ├── util.py │ └── utilETH.py └── okcoin │ ├── __init__.py │ ├── httpMD5Util.py │ ├── okcoinFutureAPI.py │ ├── okcoinSpotAPI.py │ ├── testOkcoin.py │ └── util.py ├── liveStrategyEngine ├── BaseLiveStrategyEngine.py └── __init__.py ├── log └── dummy_file ├── main_banZhuan.py ├── main_userStrategy.py ├── readme.txt ├── run_banZhuan.bat ├── run_banZhuan.sh ├── run_userStrategy.bat ├── run_userStrategy.sh ├── userStrategy ├── DualThrust.py ├── FixedPosValueGrowth.py ├── Grid.py ├── SeaTurtle.py ├── SimpleMA.py └── __init__.py └── utils ├── __init__.py ├── errors.py └── helper.py /.gitignore: -------------------------------------------------------------------------------- 1 | data/ 2 | log/ 3 | .idea/ 4 | accountConfig.py -------------------------------------------------------------------------------- /accountConfig.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # huobi config 5 | HUOBI = { 6 | "CNY_1": 7 | { 8 | "ACCESS_KEY": "", 9 | "SECRET_KEY": "", 10 | "SERVICE_API": "https://api.huobi.com/apiv3", 11 | }, 12 | "USD_1": 13 | { 14 | "ACCESS_KEY": "", 15 | "SECRET_KEY": "", 16 | "SERVICE_API": "https://api.huobi.com/apiv3", 17 | }, 18 | } 19 | 20 | # bitvc config 21 | BITVC = { 22 | "CNY_1": 23 | { 24 | "ACCESS_KEY": "", 25 | "SECRET_KEY": "", 26 | "SERVICE_API": "https://api.bitvc.com/api/", 27 | "FUTURE_SERVICE_API": "https://api.bitvc.com/futures/" 28 | } 29 | } 30 | 31 | # okcoin config 32 | OKCOIN = { 33 | "CNY_1": 34 | { 35 | "ACCESS_KEY": "", 36 | "SECRET_KEY": "", 37 | "SERVICE_API": "https://www.okcoin.cn", # okcoin国内站 38 | }, 39 | 40 | "USD_1": 41 | { 42 | "ACCESS_KEY": "", 43 | "SECRET_KEY": "", 44 | "SERVICE_API": "https://www.okcoin.com", # okcoin国际站 45 | }, 46 | } 47 | 48 | # btcc config 49 | BTCC = { 50 | "CNY_1": 51 | { 52 | "ACCESS_KEY": "", 53 | "SECRET_KEY": "", 54 | "SERVICE_API": "", 55 | }, 56 | 57 | "USD_1": 58 | { 59 | "ACCESS_KEY": "", 60 | "SECRET_KEY": "", 61 | "SERVICE_API": "", 62 | }, 63 | } -------------------------------------------------------------------------------- /banZhuan/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /banZhuan/banZhuanStrategy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | import datetime 11 | import logging 12 | 13 | from common.Errors import StartRunningTimeEmptyError 14 | from exchangeConnection.huobi import huobiService 15 | from exchangeConnection.huobi.util import * 16 | from exchangeConnection.okcoin.util import getOkcoinSpot 17 | from utils import helper 18 | 19 | 20 | class BanZhuanStrategy(object): 21 | def __init__(self, startRunningTime, orderRatio, timeInterval, orderWaitingTime, dataLogFixedTimeWindow, 22 | coinMarketType, 23 | dailyExitTime=None): 24 | self.startRunningTime = startRunningTime 25 | self.orderRatio = orderRatio # 每次预计能吃到的盘口深度的百分比 26 | self.timeInterval = timeInterval # 每次循环结束之后睡眠的时间, 单位:秒 27 | self.orderWaitingTime = orderWaitingTime # 每次等待订单执行的最长时间 28 | self.dataLogFixedTimeWindow = dataLogFixedTimeWindow # in seconds 29 | self.coinMarketType = coinMarketType 30 | self.dailyExitTime = dailyExitTime 31 | self.TimeFormatForFileName = "%Y%m%d%H%M%S%f" 32 | self.TimeFormatForLog = "%Y-%m-%d %H:%M:%S.%f" 33 | self.okcoinService = getOkcoinSpot() 34 | self.huobiService = huobiService 35 | self.huobi_min_quantity = self.huobiService.getMinimumOrderQty( 36 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"]) 37 | self.huobi_min_cash_amount = self.huobiService.getMinimumOrderCashAmount() 38 | self.okcoin_min_quantity = self.okcoinService.getMinimumOrderQty( 39 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"]) 40 | # okcoin 的比特币最小市价买单下单金额是:0.01BTC*比特币当时市价 41 | # okcoin 的莱特币最小市价买单下单金额是:0.1LTC*莱特币当时市价 42 | self.last_data_log_time = None 43 | 44 | # setup timeLogger 45 | self.timeLogger = logging.getLogger('timeLog') 46 | self.timeLogger.setLevel(logging.DEBUG) 47 | self.timeLogHandler = logging.FileHandler(self.getTimeLogFileName()) 48 | self.timeLogHandler.setLevel(logging.DEBUG) 49 | self.consoleLogHandler = logging.StreamHandler() 50 | self.consoleLogHandler.setLevel(logging.DEBUG) 51 | # 定义handler的输出格式 52 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 53 | self.timeLogHandler.setFormatter(formatter) 54 | self.consoleLogHandler.setFormatter(formatter) 55 | # 给timeLogger添加handler 56 | self.timeLogger.addHandler(self.timeLogHandler) 57 | self.timeLogger.addHandler(self.consoleLogHandler) 58 | 59 | # setup dataLogger 60 | self.dataLogger = logging.getLogger('dataLog') 61 | self.dataLogger.setLevel(logging.DEBUG) 62 | self.dataLogHandler = logging.FileHandler(self.getDataLogFileName()) 63 | self.dataLogHandler.setLevel(logging.DEBUG) 64 | self.dataLogger.addHandler(self.dataLogHandler) 65 | 66 | def getStartRunningTime(self): 67 | if self.startRunningTime == None: 68 | raise StartRunningTimeEmptyError("startRunningTime is not set yet!") 69 | return self.startRunningTime 70 | 71 | def getTimeLogFileName(self): 72 | return "log/%s_log_%s.txt" % ( 73 | self.__class__.__name__, self.getStartRunningTime().strftime(self.TimeFormatForFileName)) 74 | 75 | def getDataLogFileName(self): 76 | return "data/%s_data_%s.data" % ( 77 | self.__class__.__name__, self.getStartRunningTime().strftime(self.TimeFormatForFileName)) 78 | 79 | def timeLog(self, content): 80 | self.timeLogger.info(content) 81 | 82 | def getAccuntInfo(self): 83 | huobiAcct = self.huobiService.getAccountInfo(helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 84 | ACCOUNT_INFO) 85 | huobi_cny_cash = float(huobiAcct[u'available_cny_display']) 86 | huobi_cny_btc = float(huobiAcct[u'available_btc_display']) 87 | huobi_cny_ltc = float(huobiAcct[u'available_ltc_display']) 88 | huobi_cny_cash_loan = float(huobiAcct[u'loan_cny_display']) 89 | huobi_cny_btc_loan = float(huobiAcct[u'loan_btc_display']) 90 | huobi_cny_ltc_loan = float(huobiAcct[u'loan_ltc_display']) 91 | huobi_cny_cash_frozen = float(huobiAcct[u'frozen_cny_display']) 92 | huobi_cny_btc_frozen = float(huobiAcct[u'frozen_btc_display']) 93 | huobi_cny_ltc_frozen = float(huobiAcct[u'frozen_ltc_display']) 94 | huobi_cny_total = float(huobiAcct[u'total']) 95 | huobi_cny_net = float(huobiAcct[u'net_asset']) 96 | 97 | okcoinAcct = self.okcoinService.userinfo() 98 | okcoin_cny_cash = float(okcoinAcct["info"]["funds"]["free"]["cny"]) 99 | okcoin_cny_btc = float(okcoinAcct["info"]["funds"]["free"]["btc"]) 100 | okcoin_cny_ltc = float(okcoinAcct["info"]["funds"]["free"]["ltc"]) 101 | okcoin_cny_cash_frozen = float(okcoinAcct["info"]["funds"]["freezed"]["cny"]) 102 | okcoin_cny_btc_frozen = float(okcoinAcct["info"]["funds"]["freezed"]["btc"]) 103 | okcoin_cny_ltc_frozen = float(okcoinAcct["info"]["funds"]["freezed"]["ltc"]) 104 | okcoin_cny_total = float(okcoinAcct["info"]["funds"]["asset"]["total"]) 105 | okcoin_cny_net = float(okcoinAcct["info"]["funds"]["asset"]["net"]) 106 | total_net = huobi_cny_net + okcoin_cny_net 107 | return { 108 | "huobi_cny_cash": huobi_cny_cash, 109 | "huobi_cny_btc": huobi_cny_btc, 110 | "huobi_cny_ltc": huobi_cny_ltc, 111 | "huobi_cny_cash_loan": huobi_cny_cash_loan, 112 | "huobi_cny_btc_loan": huobi_cny_btc_loan, 113 | "huobi_cny_ltc_loan": huobi_cny_ltc_loan, 114 | "huobi_cny_cash_frozen": huobi_cny_cash_frozen, 115 | "huobi_cny_btc_frozen": huobi_cny_btc_frozen, 116 | "huobi_cny_ltc_frozen": huobi_cny_ltc_frozen, 117 | "huobi_cny_total": huobi_cny_total, 118 | "huobi_cny_net": huobi_cny_net, 119 | 120 | "okcoin_cny_cash": okcoin_cny_cash, 121 | "okcoin_cny_btc": okcoin_cny_btc, 122 | "okcoin_cny_ltc": okcoin_cny_ltc, 123 | "okcoin_cny_cash_frozen": okcoin_cny_cash_frozen, 124 | "okcoin_cny_btc_frozen": okcoin_cny_btc_frozen, 125 | "okcoin_cny_ltc_frozen": okcoin_cny_ltc_frozen, 126 | "okcoin_cny_total": okcoin_cny_total, 127 | "okcoin_cny_net": okcoin_cny_net, 128 | 129 | "total_net": total_net, 130 | } 131 | 132 | def dataLog(self, content=None): 133 | if content is None: 134 | accountInfo = self.getAccuntInfo() 135 | t = datetime.datetime.now() 136 | content = "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" % \ 137 | (t.strftime(self.TimeFormatForLog), 138 | accountInfo["huobi_cny_cash"], 139 | accountInfo["huobi_cny_btc"], 140 | accountInfo["huobi_cny_ltc"], 141 | accountInfo["huobi_cny_cash_loan"], 142 | accountInfo["huobi_cny_btc_loan"], 143 | accountInfo["huobi_cny_ltc_loan"], 144 | accountInfo["huobi_cny_cash_frozen"], 145 | accountInfo["huobi_cny_btc_frozen"], 146 | accountInfo["huobi_cny_ltc_frozen"], 147 | accountInfo["huobi_cny_total"], 148 | accountInfo["huobi_cny_net"], 149 | accountInfo["okcoin_cny_cash"], 150 | accountInfo["okcoin_cny_btc"], 151 | accountInfo["okcoin_cny_ltc"], 152 | accountInfo["okcoin_cny_cash_frozen"], 153 | accountInfo["okcoin_cny_btc_frozen"], 154 | accountInfo["okcoin_cny_ltc_frozen"], 155 | accountInfo["okcoin_cny_total"], 156 | accountInfo["okcoin_cny_net"], 157 | accountInfo["total_net"]) 158 | self.last_data_log_time = t 159 | self.dataLogger.info("%s" % str(content)) 160 | 161 | def sell(self, security, quantity, exchange="huobi"): # quantity is a string value 162 | if exchange == "huobi": 163 | self.timeLog("开始下达火币市价卖单...") 164 | self.timeLog("只保留下单数量的小数点后4位...") 165 | self.timeLog("原始下单数量:%s" % quantity) 166 | tmp = float(quantity) 167 | tmp = helper.downRound(tmp, 4) 168 | quantity = str(tmp) 169 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 170 | if float(quantity) < self.huobi_min_quantity: 171 | self.timeLog( 172 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.huobi_min_quantity)) 173 | return None 174 | 175 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 176 | res = self.huobiService.sellMarket(coin_type, quantity, None, None, 177 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 178 | SELL_MARKET) 179 | if u"result" not in res or res[u"result"] != u'success': 180 | self.timeLog("下达火币市价卖单(数量:%s)失败!" % quantity) 181 | return None 182 | order_id = res[u"id"] 183 | # 查询订单执行情况 184 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 185 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 186 | "market"], ORDER_INFO) 187 | self.timeLog("下达如下火币市价卖单,数量:%s" % quantity) 188 | self.timeLog(str(order_info)) 189 | if order_info["status"] != 2: 190 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 191 | time.sleep(self.orderWaitingTime) 192 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 193 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 194 | "market"], ORDER_INFO) 195 | self.timeLog(str(order_info)) 196 | executed_qty = float(order_info["processed_amount"]) 197 | self.timeLog( 198 | "火币市价卖单已被执行,执行数量:%f,收到的现金:%.2f" % (executed_qty, executed_qty * float(order_info["processed_price"]))) 199 | self.dataLog() 200 | return executed_qty 201 | elif exchange == "okcoin": 202 | self.timeLog("开始下达okcoin市价卖单...") 203 | self.timeLog("只保留下单数量的小数点后2位...") 204 | self.timeLog("原始下单数量:%s" % quantity) 205 | tmp = float(quantity) 206 | tmp = helper.downRound(tmp, 2) 207 | quantity = str(tmp) 208 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 209 | if float(quantity) < self.okcoin_min_quantity: 210 | self.timeLog( 211 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.okcoin_min_quantity)) 212 | return None 213 | res = self.okcoinService.trade(helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"], 214 | "sell_market", amount=quantity) 215 | if "result" not in res or res["result"] != True: 216 | self.timeLog("下达okcoin市价卖单(数量:%s)失败" % quantity) 217 | return None 218 | order_id = res["order_id"] 219 | # 查询订单执行情况 220 | order_info = self.okcoinService.orderinfo( 221 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"], str(order_id)) 222 | self.timeLog("下达如下okcoin市价卖单,数量:%s" % quantity) 223 | self.timeLog(str(order_info)) 224 | if order_info["orders"][0]["status"] != 2: 225 | self.timeLog("等待%.1f秒直至订单完成" % self.orderWaitingTime) 226 | time.sleep(self.orderWaitingTime) 227 | order_info = self.okcoinService.orderinfo( 228 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"], str(order_id)) 229 | self.timeLog(str(order_info)) 230 | executed_qty = order_info["orders"][0]["deal_amount"] 231 | self.timeLog("okcoin市价卖单已被执行,执行数量:%f,收到的现金:%.2f" % ( 232 | executed_qty, executed_qty * order_info["orders"][0]["avg_price"])) 233 | self.dataLog() 234 | return executed_qty 235 | 236 | def buy(self, security, cash_amount, exchange="huobi", sell_1_price=None): # cash_amount is a string value 237 | if exchange == "huobi": 238 | self.timeLog("开始下达火币市价买单...") 239 | self.timeLog("只保留下单数量的小数点后2位...") 240 | self.timeLog("原始下单金额:%s" % cash_amount) 241 | tmp = float(cash_amount) 242 | tmp = helper.downRound(tmp, 2) 243 | cash_amount = str(tmp) 244 | self.timeLog("做完小数点处理后的下单金额:%s" % cash_amount) 245 | 246 | if float(cash_amount) < self.huobi_min_cash_amount: 247 | self.timeLog("金额:%s 小于交易所最小交易金额(火币最小金额:1元),因此无法下单,此处忽略该信号" % (cash_amount, self.huobi_min_cash_amount)) 248 | return None 249 | 250 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 251 | res = self.huobiService.buyMarket(coin_type, cash_amount, None, None, 252 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 253 | BUY_MARKET) 254 | if u"result" not in res or res[u"result"] != u'success': 255 | self.timeLog("下达火币市价买单(金额:%s)失败!" % cash_amount) 256 | return None 257 | order_id = res[u"id"] 258 | # 查询订单执行情况 259 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 260 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 261 | "market"], ORDER_INFO) 262 | self.timeLog("下达如下火币市价买单,金额:%s" % cash_amount) 263 | self.timeLog(str(order_info)) 264 | if order_info["status"] != 2: 265 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 266 | time.sleep(self.orderWaitingTime) 267 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 268 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 269 | "market"], ORDER_INFO) 270 | self.timeLog(str(order_info)) 271 | executed_qty = float(order_info["processed_amount"]) / float(order_info["processed_price"]) 272 | self.timeLog("火币市价买单已被执行,执行数量:%f,花费的现金:%.2f" % (executed_qty, float(order_info["processed_amount"]))) 273 | self.dataLog() 274 | return executed_qty 275 | elif exchange == "okcoin": 276 | if sell_1_price is None: 277 | raise ValueError("处理okcoin市价买单之前,需要提供当前Okcoin卖一的价格信息,请检查传入的sell_1_price参数是否完备!") 278 | self.timeLog("开始下达okcoin市价买单...") 279 | self.timeLog("只保留下单数量的小数点后2位...") 280 | self.timeLog("原始下单金额:%s" % cash_amount) 281 | tmp = float(cash_amount) 282 | tmp = helper.downRound(tmp, 2) 283 | cash_amount = str(tmp) 284 | self.timeLog("做完小数点处理后的下单金额:%s" % cash_amount) 285 | 286 | if float(cash_amount) < self.okcoin_min_quantity * sell_1_price: 287 | self.timeLog( 288 | "金额:%s 不足以购买交易所最小交易数量(okcoin最小数量:%f,当前卖一价格%.2f,最小金额要求:%.2f),因此无法下单,此处忽略该信号" % ( 289 | cash_amount, self.okcoin_min_quantity, sell_1_price, self.okcoin_min_quantity * sell_1_price)) 290 | return None 291 | res = self.okcoinService.trade(helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"], 292 | "buy_market", price=cash_amount) 293 | 294 | if "result" not in res or res["result"] != True: 295 | self.timeLog("下达okcoin市价买单(金额:%s)失败" % cash_amount) 296 | return None 297 | order_id = res["order_id"] 298 | # 查询订单执行情况 299 | order_info = self.okcoinService.orderinfo( 300 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"], str(order_id)) 301 | self.timeLog("下达如下okcoin市价买单,金额:%s" % cash_amount) 302 | self.timeLog(str(order_info)) 303 | if order_info["orders"][0]["status"] != 2: 304 | self.timeLog("等待%.1f秒直至订单完成" % self.orderWaitingTime) 305 | time.sleep(self.orderWaitingTime) 306 | order_info = self.okcoinService.orderinfo( 307 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"], str(order_id)) 308 | self.timeLog(str(order_info)) 309 | executed_qty = order_info["orders"][0]["deal_amount"] 310 | self.timeLog("okcoin市价买单已被执行,执行数量:%f,花费的现金:%.2f" % ( 311 | executed_qty, executed_qty * order_info["orders"][0]["avg_price"])) 312 | self.dataLog() 313 | return executed_qty 314 | 315 | def go(self): 316 | self.timeLog("日志启动于 %s" % self.getStartRunningTime().strftime(self.TimeFormatForLog)) 317 | self.dataLog( 318 | content="time|huobi_cny_cash|huobi_cny_btc|huobi_cny_ltc|huobi_cny_cash_loan|huobi_cny_btc_loan|huobi_cny_ltc_loan|huobi_cny_cash_frozen|huobi_cny_btc_frozen|huobi_cny_ltc_frozen|huobi_cny_total|huobi_cny_net|okcoin_cny_cash|okcoin_cny_btc|okcoin_cny_ltc|okcoin_cny_cash_frozen|okcoin_cny_btc_frozen|okcoin_cny_ltc_frozen|okcoin_cny_total|okcoin_cny_net|total_net") 319 | self.dataLog() 320 | 321 | while (True): 322 | # check whether current time is after the dailyExitTime, if yes, exit 323 | if self.dailyExitTime is not None and datetime.datetime.now() > datetime.datetime.strptime( 324 | datetime.date.today().strftime("%Y-%m-%d") + " " + self.dailyExitTime, 325 | "%Y-%m-%d %H:%M:%S"): 326 | self.timeLog("抵达每日终结时间:%s, 现在退出." % self.dailyExitTime) 327 | break 328 | 329 | self.timeLog("等待 %d 秒进入下一个循环..." % self.timeInterval) 330 | time.sleep(self.timeInterval) 331 | 332 | # calculate the net asset at a fixed time window 333 | time_diff = datetime.datetime.now() - self.last_data_log_time 334 | if time_diff.seconds > self.dataLogFixedTimeWindow: 335 | self.dataLog() 336 | 337 | # 获取当前账户信息 338 | accountInfo = self.getAccuntInfo() 339 | 340 | # 查询huobi第一档深度数据 341 | huobiDepth = self.huobiService.getDepth(helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"], 342 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 343 | depth_size=1) 344 | # 查询okcoin第一档深度数据 345 | okcoinDepth = self.okcoinService.depth(helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"]) 346 | 347 | huobi_sell_1_price = huobiDepth["asks"][0][0] 348 | huobi_sell_1_qty = huobiDepth["asks"][0][1] 349 | huobi_buy_1_price = huobiDepth["bids"][0][0] 350 | huobi_buy_1_qty = huobiDepth["bids"][0][1] 351 | 352 | okcoin_sell_1_price = okcoinDepth["asks"][0][0] 353 | okcoin_sell_1_qty = okcoinDepth["asks"][0][1] 354 | okcoin_buy_1_price = okcoinDepth["bids"][0][0] 355 | okcoin_buy_1_qty = okcoinDepth["bids"][0][1] 356 | 357 | if huobi_buy_1_price > okcoin_sell_1_price: # 获利信号:OKcoin买,huobi卖 358 | self.timeLog("发现信号") 359 | self.timeLog("火币深度:%s" % str(huobiDepth)) 360 | self.timeLog("okcoin深度:%s" % str(okcoinDepth)) 361 | 362 | # 每次只能吃掉一定ratio的深度 363 | Qty = helper.downRound(min(huobi_buy_1_qty, okcoin_sell_1_qty) * self.orderRatio, 4) 364 | # 每次搬砖前要检查是否有足够security和cash 365 | Qty = min(Qty, accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_str"]], 366 | helper.downRound(accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"][ 367 | "market_str"]] / okcoin_sell_1_price, 4)) 368 | Qty = helper.downRound(Qty, 4) 369 | Qty = helper.getRoundedQuantity(Qty, self.coinMarketType) 370 | 371 | if Qty < self.huobi_min_quantity or Qty < self.okcoin_min_quantity: 372 | self.timeLog( 373 | "数量:%f 小于交易所最小交易数量(火币最小数量:%f, okcoin最小数量:%f),因此无法下单并忽略该信号" % ( 374 | Qty, self.huobi_min_quantity, self.okcoin_min_quantity)) 375 | continue 376 | else: 377 | # step1: 先处理卖 378 | executed_qty = self.sell(self.coinMarketType, str(Qty), exchange="huobi") 379 | if executed_qty is not None: 380 | # step2: 再执行买 381 | Qty2 = min(executed_qty, Qty) 382 | Qty2 = max(helper.getRoundedQuantity(Qty2, self.coinMarketType), self.okcoin_min_quantity) 383 | 384 | if Qty2 < self.okcoin_min_quantity * 1.05: 385 | self.buy(self.coinMarketType, str(Qty2 * okcoin_sell_1_price * 1.05), exchange="okcoin", 386 | sell_1_price=okcoin_sell_1_price) 387 | else: 388 | self.buy(self.coinMarketType, str(Qty2 * okcoin_sell_1_price), exchange="okcoin", 389 | sell_1_price=okcoin_sell_1_price) 390 | 391 | elif okcoin_buy_1_price > huobi_sell_1_price: # 获利信号:OKcoin卖,huobi买 392 | self.timeLog("发现信号") 393 | self.timeLog("火币深度:%s" % str(huobiDepth)) 394 | self.timeLog("okcoin深度:%s" % str(okcoinDepth)) 395 | 396 | # 每次只能吃掉一定ratio的深度 397 | Qty = helper.downRound(min(huobi_sell_1_qty, okcoin_buy_1_qty) * self.orderRatio, 4) 398 | # 每次搬砖前要检查是否有足够security和cash 399 | Qty = min(Qty, accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_str"]], 400 | helper.downRound(accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"][ 401 | "market_str"]] / huobi_sell_1_price), 4) 402 | Qty = helper.getRoundedQuantity(Qty, self.coinMarketType) 403 | 404 | if Qty < self.huobi_min_quantity or Qty < self.okcoin_min_quantity: 405 | self.timeLog("数量:%f 小于交易所最小交易数量(火币最小数量:%f, okcoin最小数量:%f),因此无法下单并忽略该信号" % ( 406 | Qty, self.huobi_min_quantity, self.okcoin_min_quantity)) 407 | continue 408 | else: 409 | # step1: 先处理卖 410 | executed_qty = self.sell(self.coinMarketType, str(Qty), exchange="okcoin") 411 | if executed_qty is not None: 412 | # step2: 再执行买 413 | Qty2 = min(executed_qty, Qty) 414 | self.buy(self.coinMarketType, str(Qty2 * huobi_sell_1_price), exchange="huobi") 415 | -------------------------------------------------------------------------------- /banZhuan/fixedSpreadArbStrategy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from banZhuan.statArbStrategy import * 5 | 6 | 7 | ############################################################### 8 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 9 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 10 | # 沉迷量化,无法自拔 11 | ############################################################### 12 | 13 | 14 | class FixedSpreadSignalGenerator(StatArbSignalGenerator): 15 | def __init__(self, startRunningTime, orderRatio, timeInterval, orderWaitingTime, dataLogFixedTimeWindow, 16 | coinMarketType, open_diff, close_diff, maximum_qty_multiplier=None, auto_rebalance_on=False, 17 | auto_rebalance_on_exit=False, 18 | dailyExitTime=None): 19 | super(FixedSpreadSignalGenerator, self).__init__(startRunningTime, orderRatio, timeInterval, orderWaitingTime, 20 | dataLogFixedTimeWindow, 21 | coinMarketType, 22 | maximum_qty_multiplier=maximum_qty_multiplier, 23 | auto_rebalance_on=auto_rebalance_on, 24 | auto_rebalance_on_exit=auto_rebalance_on_exit, 25 | dailyExitTime=dailyExitTime) 26 | self.open_diff = open_diff 27 | self.close_diff = close_diff 28 | 29 | # 判断开仓、平仓 30 | def in_or_out(self, ref_price): 31 | if self.current_position_direction == 0: # currently no spread position 32 | if self.spread1List[-1] / ref_price > self.open_diff: # huobi > okcoin 33 | return 1 # buy okcoin, sell huobi 34 | elif self.spread2List[-1] / ref_price > self.open_diff: # okcoin > huobi 35 | return 2 # sell okcoin, buy huobi 36 | elif self.current_position_direction == 1: # currently long spread1 37 | if self.spread1List[-1] / ref_price > self.open_diff: # huobi > okcoin 38 | return 1 # continue to buy okcoin, sell huobi, meaning upsizing spread1 39 | if self.spread2List[-1] / ref_price > -self.close_diff: # spread1 close is using the price data of spread2, so check spread2 here 40 | return 2 # unwind spread1 41 | elif self.current_position_direction == 2: # currently long spread1 42 | if self.spread2List[-1] / ref_price > self.open_diff: # okcoin > huobi 43 | return 2 # continue to sell okcoin, buy huobi, meaning upsizing spread2 44 | if self.spread1List[-1] / ref_price > -self.close_diff: # spread2 close is using the price data of spread1, so check spread1 here 45 | return 1 # unwind spread2 46 | return 0 # no action 47 | 48 | # 主要的逻辑 49 | def go(self): 50 | self.timeLog("日志启动于 %s" % self.getStartRunningTime().strftime(self.TimeFormatForLog)) 51 | self.dataLog( 52 | content="time|huobi_cny_cash|huobi_cny_btc|huobi_cny_ltc|huobi_cny_cash_loan|huobi_cny_btc_loan|huobi_cny_ltc_loan|huobi_cny_cash_frozen|huobi_cny_btc_frozen|huobi_cny_ltc_frozen|huobi_cny_total|huobi_cny_net|okcoin_cny_cash|okcoin_cny_btc|okcoin_cny_ltc|okcoin_cny_cash_frozen|okcoin_cny_btc_frozen|okcoin_cny_ltc_frozen|okcoin_cny_total|okcoin_cny_net|total_net") 53 | self.dataLog() 54 | while True: 55 | if self.timeInterval > 0: 56 | self.timeLog("等待 %d 秒进入下一个循环..." % self.timeInterval) 57 | time.sleep(self.timeInterval) 58 | 59 | self.timeLog("记录心跳信息...") 60 | 61 | # calculate the net asset at a fixed time window 62 | time_diff = datetime.datetime.now() - self.last_data_log_time 63 | if time_diff.seconds > self.dataLogFixedTimeWindow: 64 | self.dataLog() 65 | 66 | # 查询huobi第一档深度数据 67 | huobiDepth = self.HuobiService.getDepth(helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"], 68 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 69 | depth_size=1) 70 | # 查询okcoin第一档深度数据 71 | okcoinDepth = self.OKCoinService.depth(helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"]) 72 | 73 | huobi_sell_1_price = huobiDepth["asks"][0][0] 74 | huobi_sell_1_qty = huobiDepth["asks"][0][1] 75 | huobi_buy_1_price = huobiDepth["bids"][0][0] 76 | huobi_buy_1_qty = huobiDepth["bids"][0][1] 77 | okcoin_sell_1_price = okcoinDepth["asks"][0][0] 78 | okcoin_sell_1_qty = okcoinDepth["asks"][0][1] 79 | okcoin_buy_1_price = okcoinDepth["bids"][0][0] 80 | okcoin_buy_1_qty = okcoinDepth["bids"][0][1] 81 | spread1 = huobi_buy_1_price - okcoin_sell_1_price 82 | spread2 = okcoin_buy_1_price - huobi_sell_1_price 83 | self.spread1List = self.add_to_list(self.spread1List, spread1, 1) 84 | self.spread2List = self.add_to_list(self.spread2List, spread2, 1) 85 | max_price = np.max([huobi_sell_1_price, huobi_buy_1_price, okcoin_sell_1_price, okcoin_buy_1_price]) 86 | 87 | # 获取当前账户信息 88 | accountInfo = self.getAccuntInfo() 89 | 90 | # check whether current time is after the dailyExitTime, if yes, exit 91 | if self.dailyExitTime is not None and datetime.datetime.now() > datetime.datetime.strptime( 92 | datetime.date.today().strftime("%Y-%m-%d") + " " + self.dailyExitTime, 93 | "%Y-%m-%d %H:%M:%S"): 94 | self.timeLog("抵达每日终结时间:%s, 现在退出." % self.dailyExitTime) 95 | if self.auto_rebalance_on_exit: 96 | self.rebalance_position(accountInfo, max_price) 97 | break 98 | 99 | current_position_proportion = self.get_current_position_proportion(accountInfo, max_price) 100 | if self.auto_rebalance_on and current_position_proportion > self.position_proportion_threshold: 101 | if abs(self.spread1List[-1] / max_price) < self.close_diff or abs( 102 | self.spread2List[-1] / max_price) < self.close_diff: 103 | self.rebalance_position(accountInfo, max_price) 104 | continue 105 | 106 | in_or_out = self.in_or_out(max_price) 107 | if in_or_out == 0: 108 | self.timeLog("没有发现交易信号,进入下一个轮询...") 109 | continue 110 | elif in_or_out == 1: 111 | if self.current_position_direction == 0: # 当前没有持仓 112 | self.timeLog("发现开仓信号(火币卖,OKcoin买)") 113 | elif self.current_position_direction == 1: # 当前long spread1 114 | self.timeLog("发现增仓信号(火币卖,OKcoin买)") 115 | elif self.current_position_direction == 2: # 当前long spread2 116 | self.timeLog("发现减仓信号(火币卖,OKcoin买)") 117 | 118 | self.timeLog("火币深度:%s" % str(huobiDepth)) 119 | self.timeLog("okcoin深度:%s" % str(okcoinDepth)) 120 | 121 | if self.current_position_direction == 0 or self.current_position_direction == 1: 122 | if current_position_proportion > self.position_proportion_alert_threshold: 123 | self.timeLog( 124 | "当前仓位比例:%f 大于仓位预警比例:%f" % ( 125 | current_position_proportion, self.position_proportion_alert_threshold), 126 | level=logging.WARN) 127 | # 每次只能吃掉一定ratio的深度 128 | Qty = helper.downRound(min(huobi_buy_1_qty, okcoin_sell_1_qty) * self.orderRatio, 4) 129 | elif self.current_position_direction == 2: 130 | depthQty = helper.downRound(min(huobi_buy_1_qty, okcoin_sell_1_qty) * self.orderRatio, 4) 131 | Qty = min(depthQty, helper.downRound(self.spread2_pos_qty, 4)) 132 | 133 | # 每次搬砖最多只搬maximum_qty_multiplier个最小单位 134 | if self.maximum_qty_multiplier is not None: 135 | Qty = min(Qty, max(self.huobi_min_quantity, self.okcoin_min_quantity) * self.maximum_qty_multiplier) 136 | 137 | # 每次搬砖前要检查是否有足够security和cash 138 | Qty = min(Qty, accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_str"]], 139 | helper.downRound(accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"][ 140 | "market_str"]] / okcoin_sell_1_price, 4)) 141 | Qty = helper.downRound(Qty, 4) 142 | Qty = helper.getRoundedQuantity(Qty, self.coinMarketType) 143 | 144 | if Qty < self.huobi_min_quantity or Qty < self.okcoin_min_quantity: 145 | self.timeLog("当前在火币的币量:%.4f,OKCoin的现金:%.2f" % ( 146 | accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_str"]], 147 | accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"]["market_str"]])) 148 | self.timeLog("可交易的数量:%.4f 小于交易所最小交易数量(火币最小数量:%.4f, OKCoin最小数量:%.4f),因此无法下单并忽略该信号" % ( 149 | Qty, self.huobi_min_quantity, self.okcoin_min_quantity), level=logging.WARN) 150 | continue 151 | else: 152 | # step1: 先处理卖 153 | executed_qty = self.sell_market(self.coinMarketType, str(Qty), exchange="huobi") 154 | if executed_qty is not None: 155 | # step2: 再执行买 156 | Qty2 = min(executed_qty, Qty) 157 | Qty2 = max(helper.getRoundedQuantity(Qty2, self.coinMarketType), self.okcoin_min_quantity) 158 | 159 | if Qty2 < self.okcoin_min_quantity * 1.05: 160 | self.buy_market(self.coinMarketType, str(Qty2 * okcoin_sell_1_price * 1.05), 161 | exchange="okcoin", 162 | sell_1_price=okcoin_sell_1_price) 163 | else: 164 | self.buy_market(self.coinMarketType, str(Qty2 * okcoin_sell_1_price), exchange="okcoin", 165 | sell_1_price=okcoin_sell_1_price) 166 | 167 | if self.current_position_direction == 0 or self.current_position_direction == 1: 168 | self.spread1_pos_qty += Qty2 169 | elif self.current_position_direction == 2: 170 | self.spread2_pos_qty -= Qty2 171 | elif in_or_out == 2: 172 | if self.current_position_direction == 0: # 当前没有持仓 173 | self.timeLog("发现开仓信号(OKcoin卖, 火币买)") 174 | elif self.current_position_direction == 2: # 当前long spread2 175 | self.timeLog("发现增仓信号(OKcoin卖, 火币买)") 176 | elif self.current_position_direction == 1: # 当前long spread1 177 | self.timeLog("发现减仓信号(OKcoin卖, 火币买)") 178 | 179 | self.timeLog("火币深度:%s" % str(huobiDepth)) 180 | self.timeLog("okcoin深度:%s" % str(okcoinDepth)) 181 | 182 | if self.current_position_direction == 0 or self.current_position_direction == 2: 183 | if current_position_proportion > self.position_proportion_alert_threshold: 184 | self.timeLog( 185 | "当前仓位比例:%f 大于仓位预警比例:%f" % ( 186 | current_position_proportion, self.position_proportion_alert_threshold), 187 | level=logging.WARN) 188 | # 每次只能吃掉一定ratio的深度 189 | Qty = helper.downRound(min(huobi_sell_1_qty, okcoin_buy_1_qty) * self.orderRatio, 4) 190 | elif self.current_position_direction == 1: 191 | depthQty = helper.downRound(min(huobi_sell_1_qty, okcoin_buy_1_qty) * self.orderRatio, 4) 192 | Qty = min(depthQty, helper.downRound(self.spread1_pos_qty, 4)) 193 | 194 | # 每次搬砖最多只搬maximum_qty_multiplier个最小单位 195 | if self.maximum_qty_multiplier is not None: 196 | Qty = min(Qty, max(self.huobi_min_quantity, self.okcoin_min_quantity) * self.maximum_qty_multiplier) 197 | 198 | # 每次搬砖前要检查是否有足够security和cash 199 | Qty = min(Qty, accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_str"]], 200 | helper.downRound(accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"][ 201 | "market_str"]] / huobi_sell_1_price), 4) 202 | Qty = helper.getRoundedQuantity(Qty, self.coinMarketType) 203 | 204 | if Qty < self.huobi_min_quantity or Qty < self.okcoin_min_quantity: 205 | self.timeLog("当前在OKCoin的币量:%.4f,火币的现金:%.2f" % ( 206 | accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_str"]], 207 | accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"]["market_str"]])) 208 | self.timeLog("可交易数量:%.4f 小于交易所最小交易数量(火币最小数量:%.4f, OKCoin最小数量:%.4f),因此无法下单并忽略该信号" % ( 209 | Qty, self.huobi_min_quantity, self.okcoin_min_quantity), level=logging.WARN) 210 | continue 211 | else: 212 | # step1: 先处理卖 213 | executed_qty = self.sell_market(self.coinMarketType, str(Qty), exchange="okcoin") 214 | if executed_qty is not None: 215 | # step2: 再执行买 216 | Qty2 = min(executed_qty, Qty) 217 | self.buy_market(self.coinMarketType, str(Qty2 * huobi_sell_1_price), exchange="huobi") 218 | if self.current_position_direction == 0 or self.current_position_direction == 2: 219 | self.spread2_pos_qty += Qty2 220 | elif self.current_position_direction == 1: 221 | self.spread1_pos_qty -= Qty2 222 | 223 | if self.spread1_pos_qty > self.spread_pos_qty_minimum_size: 224 | self.current_position_direction = 1 225 | elif self.spread2_pos_qty > self.spread_pos_qty_minimum_size: 226 | self.current_position_direction = 2 227 | else: 228 | self.current_position_direction = 0 229 | -------------------------------------------------------------------------------- /banZhuan/statArbStrategy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | import datetime 11 | import logging 12 | 13 | import numpy as np 14 | 15 | from exchangeConnection.huobi import huobiService 16 | from exchangeConnection.huobi.util import * 17 | from exchangeConnection.okcoin.util import getOkcoinSpot 18 | from utils import helper 19 | from utils.errors import StartRunningTimeEmptyError 20 | 21 | 22 | class StatArbSignalGenerator(object): 23 | def __init__(self, startRunningTime, orderRatio, timeInterval, orderWaitingTime, dataLogFixedTimeWindow, 24 | coinMarketType, maximum_qty_multiplier=None, auto_rebalance_on=False, 25 | auto_rebalance_on_exit=False, 26 | dailyExitTime=None): 27 | self.startRunningTime = startRunningTime 28 | self.orderRatio = orderRatio # 每次预计能吃到的盘口深度的百分比 29 | self.timeInterval = timeInterval # 每次循环结束之后睡眠的时间, 单位:秒 30 | self.orderWaitingTime = orderWaitingTime # 每次等待订单执行的最长时间 31 | self.coinMarketType = coinMarketType 32 | self.dailyExitTime = dailyExitTime 33 | self.TimeFormatForFileName = "%Y%m%d%H%M%S%f" 34 | self.TimeFormatForLog = "%Y-%m-%d %H:%M:%S.%f" 35 | self.OKCoinService = getOkcoinSpot() 36 | self.HuobiService = huobiService 37 | self.huobi_min_quantity = self.HuobiService.getMinimumOrderQty( 38 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"]) 39 | self.huobi_min_cash_amount = self.HuobiService.getMinimumOrderCashAmount() 40 | self.okcoin_min_quantity = self.OKCoinService.getMinimumOrderQty( 41 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"]) 42 | # okcoin 的比特币最小市价买单下单金额是:0.01BTC*比特币当时市价 43 | # okcoin 的莱特币最小市价买单下单金额是:0.1LTC*莱特币当时市价 44 | self.last_data_log_time = None 45 | 46 | # setup timeLogger 47 | self.timeLogger = helper.TimeLogger(self.getTimeLogFileName()) 48 | self.dataLogFixedTimeWindow = dataLogFixedTimeWindow 49 | # setup dataLogger 50 | self.dataLogger = logging.getLogger('dataLog') 51 | self.dataLogger.setLevel(logging.DEBUG) 52 | self.dataLogHandler = logging.FileHandler(self.getDataLogFileName()) 53 | self.dataLogHandler.setLevel(logging.DEBUG) 54 | self.dataLogger.addHandler(self.dataLogHandler) 55 | 56 | # spread list and other open/close conditions 57 | self.spread1List = [] # huobi_buy1 - okcoin_sell1 58 | self.spread2List = [] # okcoin_buy1 - huobi_sell1 59 | self.spread1_mean = None 60 | self.spread1_stdev = None 61 | self.spread2_mean = None 62 | self.spread2_stdev = None 63 | self.sma_window_size = 10 64 | self.spread1_open_condition_stdev_coe = 2 65 | self.spread2_open_condition_stdev_coe = 2 66 | self.spread1_close_condition_stdev_coe = 0.3 67 | self.spread2_close_condition_stdev_coe = 0.3 68 | self.current_position_direction = 0 # 1: long spread1(buy okcoin, sell huobi); 2: long spread2( buy huobi, sell okcoin), 0: no position 69 | self.spread1_pos_qty = 0 70 | self.spread2_pos_qty = 0 71 | self.position_proportion_alert_threshold = 0.80 # if position proportion > position_proportion_alert_threshold, generate alerts 72 | self.position_proportion_threshold = 0.90 # if position proportion > position_proportion_threshold, stop upsizing, only allow downsizing 73 | self.spread_pos_qty_minimum_size = max(self.okcoin_min_quantity, self.huobi_min_quantity) * 1.5 74 | 75 | # rebalance setup 76 | self.rebalanced_position_proportion = 0.5 77 | self.auto_rebalance_on = auto_rebalance_on 78 | self.auto_rebalance_on_exit = auto_rebalance_on_exit 79 | 80 | # add order query retry maximum times 81 | self.huobi_order_query_retry_maximum_times = 5 82 | self.okcoin_order_query_retry_maximum_times = 5 83 | 84 | # add order "cancellation" query retry maximum times 85 | self.huobi_order_cancel_query_retry_maximum_times = 5 86 | self.okcoin_order_cancel_query_retry_maximum_times = 5 87 | 88 | # 每次最多可以搬得最小单位倍数 89 | self.maximum_qty_multiplier = maximum_qty_multiplier 90 | 91 | def getStartRunningTime(self): 92 | if self.startRunningTime == None: 93 | raise StartRunningTimeEmptyError("startRunningTime is not set yet!") 94 | return self.startRunningTime 95 | 96 | def getTimeLogFileName(self): 97 | return "log/%s_log_%s.txt" % ( 98 | self.__class__.__name__, self.getStartRunningTime().strftime(self.TimeFormatForFileName)) 99 | 100 | def getDataLogFileName(self): 101 | return "data/%s_data_%s.data" % ( 102 | self.__class__.__name__, self.getStartRunningTime().strftime(self.TimeFormatForFileName)) 103 | 104 | def timeLog(self, content, level=logging.INFO): 105 | self.timeLogger.timeLog(content, level=level) 106 | 107 | def dataLog(self, content=None): 108 | if content is None: 109 | accountInfo = self.getAccuntInfo() 110 | t = datetime.datetime.now() 111 | content = "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" % \ 112 | (t.strftime(self.TimeFormatForLog), 113 | accountInfo["huobi_cny_cash"], 114 | accountInfo["huobi_cny_btc"], 115 | accountInfo["huobi_cny_ltc"], 116 | accountInfo["huobi_cny_cash_loan"], 117 | accountInfo["huobi_cny_btc_loan"], 118 | accountInfo["huobi_cny_ltc_loan"], 119 | accountInfo["huobi_cny_cash_frozen"], 120 | accountInfo["huobi_cny_btc_frozen"], 121 | accountInfo["huobi_cny_ltc_frozen"], 122 | accountInfo["huobi_cny_total"], 123 | accountInfo["huobi_cny_net"], 124 | accountInfo["okcoin_cny_cash"], 125 | accountInfo["okcoin_cny_btc"], 126 | accountInfo["okcoin_cny_ltc"], 127 | accountInfo["okcoin_cny_cash_frozen"], 128 | accountInfo["okcoin_cny_btc_frozen"], 129 | accountInfo["okcoin_cny_ltc_frozen"], 130 | accountInfo["okcoin_cny_total"], 131 | accountInfo["okcoin_cny_net"], 132 | accountInfo["total_net"]) 133 | self.last_data_log_time = t 134 | self.dataLogger.info("%s" % str(content)) 135 | 136 | def getAccuntInfo(self): 137 | huobiAcct = self.HuobiService.getAccountInfo(helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 138 | ACCOUNT_INFO) 139 | huobi_cny_cash = float(huobiAcct[u'available_cny_display']) 140 | huobi_cny_btc = float(huobiAcct[u'available_btc_display']) 141 | huobi_cny_ltc = float(huobiAcct[u'available_ltc_display']) 142 | huobi_cny_cash_loan = float(huobiAcct[u'loan_cny_display']) 143 | huobi_cny_btc_loan = float(huobiAcct[u'loan_btc_display']) 144 | huobi_cny_ltc_loan = float(huobiAcct[u'loan_ltc_display']) 145 | huobi_cny_cash_frozen = float(huobiAcct[u'frozen_cny_display']) 146 | huobi_cny_btc_frozen = float(huobiAcct[u'frozen_btc_display']) 147 | huobi_cny_ltc_frozen = float(huobiAcct[u'frozen_ltc_display']) 148 | huobi_cny_total = float(huobiAcct[u'total']) 149 | huobi_cny_net = float(huobiAcct[u'net_asset']) 150 | 151 | okcoinAcct = self.OKCoinService.userInfo() 152 | okcoin_cny_cash = float(okcoinAcct["info"]["funds"]["free"]["cny"]) 153 | okcoin_cny_btc = float(okcoinAcct["info"]["funds"]["free"]["btc"]) 154 | okcoin_cny_ltc = float(okcoinAcct["info"]["funds"]["free"]["ltc"]) 155 | okcoin_cny_cash_frozen = float(okcoinAcct["info"]["funds"]["freezed"]["cny"]) 156 | okcoin_cny_btc_frozen = float(okcoinAcct["info"]["funds"]["freezed"]["btc"]) 157 | okcoin_cny_ltc_frozen = float(okcoinAcct["info"]["funds"]["freezed"]["ltc"]) 158 | okcoin_cny_total = float(okcoinAcct["info"]["funds"]["asset"]["total"]) 159 | okcoin_cny_net = float(okcoinAcct["info"]["funds"]["asset"]["net"]) 160 | total_net = huobi_cny_net + okcoin_cny_net 161 | return { 162 | "huobi_cny_cash": huobi_cny_cash, 163 | "huobi_cny_btc": huobi_cny_btc, 164 | "huobi_cny_ltc": huobi_cny_ltc, 165 | "huobi_cny_cash_loan": huobi_cny_cash_loan, 166 | "huobi_cny_btc_loan": huobi_cny_btc_loan, 167 | "huobi_cny_ltc_loan": huobi_cny_ltc_loan, 168 | "huobi_cny_cash_frozen": huobi_cny_cash_frozen, 169 | "huobi_cny_btc_frozen": huobi_cny_btc_frozen, 170 | "huobi_cny_ltc_frozen": huobi_cny_ltc_frozen, 171 | "huobi_cny_total": huobi_cny_total, 172 | "huobi_cny_net": huobi_cny_net, 173 | 174 | "okcoin_cny_cash": okcoin_cny_cash, 175 | "okcoin_cny_btc": okcoin_cny_btc, 176 | "okcoin_cny_ltc": okcoin_cny_ltc, 177 | "okcoin_cny_cash_frozen": okcoin_cny_cash_frozen, 178 | "okcoin_cny_btc_frozen": okcoin_cny_btc_frozen, 179 | "okcoin_cny_ltc_frozen": okcoin_cny_ltc_frozen, 180 | "okcoin_cny_total": okcoin_cny_total, 181 | "okcoin_cny_net": okcoin_cny_net, 182 | 183 | "total_net": total_net, 184 | } 185 | 186 | # 限价卖单 187 | def sell_limit(self, security, price, quantity, exchange="huobi"): # price, quantity are in string format 188 | if exchange == "huobi": 189 | self.timeLog("开始下达火币限价卖单...") 190 | self.timeLog("只保留下单数量的小数点后4位,下单价格的小数点后2位...") 191 | self.timeLog("原始下单数量:%s" % quantity) 192 | tmp = float(quantity) 193 | tmp = helper.downRound(tmp, 4) 194 | quantity = str(tmp) 195 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 196 | if float(quantity) < self.huobi_min_quantity: 197 | self.timeLog( 198 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.huobi_min_quantity), 199 | level=logging.WARN) 200 | return None 201 | 202 | self.timeLog("原始下单价格:%s" % price) 203 | tmp = float(price) 204 | tmp = helper.downRound(tmp, 2) 205 | price = str(tmp) 206 | self.timeLog("做完小数点处理后的下单价格:%s" % price) 207 | 208 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 209 | market = helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"] 210 | 211 | res = self.HuobiService.sell(coin_type, price, quantity, None, None, market, SELL) 212 | if helper.componentExtract(res, u"result", "") != u'success': 213 | self.timeLog("下达火币限价卖单(数量:%s, 价格:%s)失败, 结果是: %s!" % ( 214 | quantity, price, helper.componentExtract(res, u"result", "")), 215 | level=logging.ERROR) 216 | return None 217 | 218 | order_id = res[u"id"] 219 | # 查询订单执行情况 220 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 221 | self.timeLog("下达如下火币限价卖单,数量:%s, 价格:%s" % (quantity, price)) 222 | self.timeLog(str(order_info)) 223 | 224 | retry_time = 0 225 | while retry_time < self.huobi_order_query_retry_maximum_times and order_info["status"] != 2: 226 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 227 | time.sleep(self.orderWaitingTime) 228 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 229 | self.timeLog(str(order_info)) 230 | retry_time += 1 231 | 232 | # if order is not fully filled, cancel order if after timeout, cancel order is synchronized 233 | if order_info["status"] != 2: 234 | cancel_result = self.HuobiService.cancelOrder(coin_type, order_id, market, CANCEL_ORDER) 235 | retry_time = 0 236 | while retry_time < self.huobi_order_cancel_query_retry_maximum_times and helper.componentExtract( 237 | cancel_result, u"result", "") != u'success': 238 | self.timeLog("等待%f秒直至订单取消完成" % self.orderWaitingTime) 239 | time.sleep(self.orderWaitingTime) 240 | cancel_result = self.HuobiService.cancelOrder(coin_type, order_id, market, CANCEL_ORDER) 241 | retry_time += 1 242 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 243 | 244 | executed_qty = float(order_info["processed_amount"]) 245 | self.timeLog( 246 | "火币限价卖单已被执行,执行数量:%f,收到的现金:%.2f" % (executed_qty, executed_qty * float(order_info["processed_price"]))) 247 | return executed_qty 248 | elif exchange == "okcoin": 249 | self.timeLog("开始下达okcoin限价卖单...") 250 | self.timeLog("只保留下单数量的小数点后2位...") 251 | self.timeLog("原始下单数量:%s" % quantity) 252 | tmp = float(quantity) 253 | tmp = helper.downRound(tmp, 2) 254 | quantity = str(tmp) 255 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 256 | if float(quantity) < self.okcoin_min_quantity: 257 | self.timeLog( 258 | "数量:%s 小于交易所最小交易数量(okcoin最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.okcoin_min_quantity), 259 | level=logging.WARN) 260 | return None 261 | 262 | self.timeLog("原始下单价格:%s" % price) 263 | tmp = float(price) 264 | tmp = helper.downRound(tmp, 2) 265 | price = str(tmp) 266 | self.timeLog("做完小数点处理后的下单价格:%s" % price) 267 | 268 | coin_type = helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"] 269 | 270 | res = self.OKCoinService.trade(coin_type, "sell", price=price, amount=quantity) 271 | if helper.componentExtract(res, "result") != True: 272 | self.timeLog( 273 | "下达okcoin限价卖单(数量:%s, 价格:%s)失败, 结果是:%s" % (quantity, price, helper.componentExtract(res, "result")), 274 | level=logging.ERROR) 275 | return None 276 | order_id = res["order_id"] 277 | # 查询订单执行情况 278 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 279 | self.timeLog("下达如下okcoin限价卖单,数量:%s, 价格:%s" % (quantity, price)) 280 | self.timeLog(str(order_info)) 281 | 282 | retry_time = 0 283 | while retry_time < self.okcoin_order_query_retry_maximum_times and order_info["orders"][0]["status"] != 2: 284 | self.timeLog("等待%.1f秒直至订单完成" % self.orderWaitingTime) 285 | time.sleep(self.orderWaitingTime) 286 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 287 | self.timeLog(str(order_info)) 288 | retry_time += 1 289 | 290 | if order_info["orders"][0]["status"] != 2: 291 | cancel_result = self.OKCoinService.cancelOrder(coin_type, str(order_id)) 292 | retry_time = 0 293 | while retry_time < self.okcoin_order_cancel_query_retry_maximum_times and helper.componentExtract( 294 | cancel_result, u"result", "") != True: 295 | self.timeLog("等待%f秒直至订单取消完成" % self.orderWaitingTime) 296 | time.sleep(self.orderWaitingTime) 297 | cancel_result = self.OKCoinService.cancelOrder(coin_type, str(order_id)) 298 | retry_time += 1 299 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 300 | 301 | executed_qty = order_info["orders"][0]["deal_amount"] 302 | self.timeLog("okcoin限价卖单已被执行,执行数量:%f,收到的现金:%.2f" % ( 303 | executed_qty, executed_qty * order_info["orders"][0]["avg_price"])) 304 | return executed_qty 305 | 306 | # 市价卖单 307 | def sell_market(self, security, quantity, exchange="huobi"): # quantity is a string value 308 | if exchange == "huobi": 309 | self.timeLog("开始下达火币市价卖单...") 310 | self.timeLog("只保留下单数量的小数点后4位...") 311 | self.timeLog("原始下单数量:%s" % quantity) 312 | tmp = float(quantity) 313 | tmp = helper.downRound(tmp, 4) 314 | quantity = str(tmp) 315 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 316 | if float(quantity) < self.huobi_min_quantity: 317 | self.timeLog( 318 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.huobi_min_quantity), 319 | level=logging.WARN) 320 | return None 321 | 322 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 323 | market = helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"] 324 | 325 | res = self.HuobiService.sellMarket(coin_type, quantity, None, None, market, SELL_MARKET) 326 | if helper.componentExtract(res, u"result", "") != u'success': 327 | self.timeLog("下达火币市价卖单(数量:%s)失败, 结果是: %s!" % (quantity, helper.componentExtract(res, u"result", "")), 328 | level=logging.ERROR) 329 | return None 330 | order_id = res[u"id"] 331 | # 查询订单执行情况 332 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 333 | self.timeLog("下达如下火币市价卖单,数量:%s" % quantity) 334 | self.timeLog(str(order_info)) 335 | 336 | retry_time = 0 337 | while retry_time < self.huobi_order_query_retry_maximum_times and order_info["status"] != 2: 338 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 339 | time.sleep(self.orderWaitingTime) 340 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 341 | self.timeLog(str(order_info)) 342 | retry_time += 1 343 | 344 | executed_qty = float(order_info["processed_amount"]) 345 | self.timeLog( 346 | "火币市价卖单已被执行,执行数量:%f,收到的现金:%.2f" % (executed_qty, executed_qty * float(order_info["processed_price"]))) 347 | return executed_qty 348 | elif exchange == "okcoin": 349 | self.timeLog("开始下达okcoin市价卖单...") 350 | self.timeLog("只保留下单数量的小数点后2位...") 351 | self.timeLog("原始下单数量:%s" % quantity) 352 | tmp = float(quantity) 353 | tmp = helper.downRound(tmp, 2) 354 | quantity = str(tmp) 355 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 356 | if float(quantity) < self.okcoin_min_quantity: 357 | self.timeLog( 358 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.okcoin_min_quantity), 359 | level=logging.WARN) 360 | return None 361 | 362 | coin_type = helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"] 363 | 364 | res = self.OKCoinService.trade(coin_type, "sell_market", amount=quantity) 365 | if helper.componentExtract(res, "result") != True: 366 | self.timeLog("下达okcoin市价卖单(数量:%s)失败, 结果是:%s" % (quantity, helper.componentExtract(res, "result")), 367 | level=logging.ERROR) 368 | return None 369 | order_id = res["order_id"] 370 | # 查询订单执行情况 371 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 372 | self.timeLog("下达如下okcoin市价卖单,数量:%s" % quantity) 373 | self.timeLog(str(order_info)) 374 | 375 | retry_time = 0 376 | while retry_time < self.okcoin_order_query_retry_maximum_times and order_info["orders"][0]["status"] != 2: 377 | self.timeLog("等待%.1f秒直至订单完成" % self.orderWaitingTime) 378 | time.sleep(self.orderWaitingTime) 379 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 380 | self.timeLog(str(order_info)) 381 | retry_time += 1 382 | 383 | executed_qty = order_info["orders"][0]["deal_amount"] 384 | self.timeLog("okcoin市价卖单已被执行,执行数量:%f,收到的现金:%.2f" % ( 385 | executed_qty, executed_qty * order_info["orders"][0]["avg_price"])) 386 | return executed_qty 387 | 388 | # 限价买单 389 | def buy_limit(self, security, price, quantity, exchange="huobi"): 390 | if exchange == "huobi": 391 | self.timeLog("开始下达火币限价买单...") 392 | self.timeLog("只保留下单数量的小数点后4位,下单价格的小数点后2位...") 393 | self.timeLog("原始下单数量:%s" % quantity) 394 | tmp = float(quantity) 395 | tmp = helper.downRound(tmp, 4) 396 | quantity = str(tmp) 397 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 398 | 399 | if float(quantity) < self.huobi_min_quantity: 400 | self.timeLog( 401 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.huobi_min_quantity), 402 | level=logging.WARN) 403 | return None 404 | 405 | self.timeLog("原始下单价格:%s" % price) 406 | tmp = float(price) 407 | tmp = helper.downRound(tmp, 2) 408 | price = str(tmp) 409 | self.timeLog("做完小数点处理后的下单价格:%s" % price) 410 | 411 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 412 | market = helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"] 413 | 414 | res = self.HuobiService.buy(coin_type, price, quantity, None, None, market, BUY) 415 | if helper.componentExtract(res, u"result", "") != u'success': 416 | self.timeLog( 417 | "下达火币限价买单(数量:%s,价格:%s)失败, 结果是:%s!" % (quantity, price, helper.componentExtract(res, u"result", "")), 418 | level=logging.ERROR) 419 | return None 420 | order_id = res[u"id"] 421 | # 查询订单执行情况 422 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 423 | self.timeLog("下达如下火币限价买单,数量:%s, 价格:%s" % (quantity, price)) 424 | self.timeLog(str(order_info)) 425 | 426 | retry_time = 0 427 | while retry_time < self.huobi_order_query_retry_maximum_times and order_info["status"] != 2: 428 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 429 | time.sleep(self.orderWaitingTime) 430 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 431 | self.timeLog(str(order_info)) 432 | retry_time += 1 433 | 434 | # if order is not fully filled, cancel order if after timeout, cancel order is synchronized 435 | if order_info["status"] != 2: 436 | cancel_result = self.HuobiService.cancelOrder(coin_type, order_id, market, CANCEL_ORDER) 437 | retry_time = 0 438 | while retry_time < self.huobi_order_cancel_query_retry_maximum_times and helper.componentExtract( 439 | cancel_result, u"result", "") != u'success': 440 | self.timeLog("等待%f秒直至订单取消完成" % self.orderWaitingTime) 441 | time.sleep(self.orderWaitingTime) 442 | cancel_result = self.HuobiService.cancelOrder(coin_type, order_id, market, CANCEL_ORDER) 443 | retry_time += 1 444 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 445 | 446 | if float(order_info["processed_price"]) > 0: 447 | executed_qty = float(order_info["processed_amount"]) 448 | self.timeLog("火币限价买单已被执行,执行数量:%f,花费的现金:%.2f" % ( 449 | executed_qty, float(order_info["processed_price"]) * executed_qty)) 450 | return executed_qty 451 | else: 452 | self.timeLog("火币限价买单未被执行", level=logging.WARN) 453 | return None 454 | 455 | elif exchange == "okcoin": 456 | self.timeLog("开始下达okcoin限价买单...") 457 | self.timeLog("只保留下单数量的小数点后2位...") 458 | self.timeLog("原始下单数量:%s" % quantity) 459 | tmp = float(quantity) 460 | tmp = helper.downRound(tmp, 2) 461 | quantity = str(tmp) 462 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 463 | if float(quantity) < self.okcoin_min_quantity: 464 | self.timeLog( 465 | "数量:%s 小于交易所最小交易数量(okcoin最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.okcoin_min_quantity), 466 | level=logging.WARN) 467 | return None 468 | 469 | self.timeLog("原始下单价格:%s" % price) 470 | tmp = float(price) 471 | tmp = helper.downRound(tmp, 2) 472 | price = str(tmp) 473 | self.timeLog("做完小数点处理后的下单价格:%s" % price) 474 | 475 | coin_type = helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"] 476 | 477 | res = self.OKCoinService.trade(coin_type, "buy", price=price, amount=quantity) 478 | 479 | if helper.componentExtract(res, "result") != True: 480 | self.timeLog( 481 | "下达okcoin限价买单(数量:%s,价格:%s)失败, 结果是:%s!" % (quantity, price, helper.componentExtract(res, "result")), 482 | level=logging.ERROR) 483 | return None 484 | 485 | order_id = res["order_id"] 486 | # 查询订单执行情况 487 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 488 | self.timeLog("下达如下okcoin限价买单,数量:%s,价格:%s" % (quantity, price)) 489 | self.timeLog(str(order_info)) 490 | 491 | retry_time = 0 492 | while retry_time < self.okcoin_order_query_retry_maximum_times and order_info["orders"][0]["status"] != 2: 493 | self.timeLog("等待%.1f秒直至订单完成" % self.orderWaitingTime) 494 | time.sleep(self.orderWaitingTime) 495 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 496 | self.timeLog(str(order_info)) 497 | retry_time += 1 498 | 499 | if order_info["orders"][0]["status"] != 2: 500 | cancel_result = self.OKCoinService.cancelOrder(coin_type, str(order_id)) 501 | retry_time = 0 502 | while retry_time < self.okcoin_order_cancel_query_retry_maximum_times and helper.componentExtract( 503 | cancel_result, u"result", "") != True: 504 | self.timeLog("等待%f秒直至订单取消完成" % self.orderWaitingTime) 505 | time.sleep(self.orderWaitingTime) 506 | cancel_result = self.OKCoinService.cancelOrder(coin_type, str(order_id)) 507 | retry_time += 1 508 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 509 | 510 | executed_qty = order_info["orders"][0]["deal_amount"] 511 | self.timeLog("okcoin限价买单已被执行,执行数量:%f,花费的现金:%.2f" % ( 512 | executed_qty, executed_qty * order_info["orders"][0]["avg_price"])) 513 | return executed_qty 514 | 515 | # 市价买单 516 | def buy_market(self, security, cash_amount, exchange="huobi", sell_1_price=None): # cash_amount is a string value 517 | if exchange == "huobi": 518 | self.timeLog("开始下达火币市价买单...") 519 | self.timeLog("只保留下单数量的小数点后2位...") 520 | self.timeLog("原始下单金额:%s" % cash_amount) 521 | tmp = float(cash_amount) 522 | tmp = helper.downRound(tmp, 2) 523 | cash_amount = str(tmp) 524 | self.timeLog("做完小数点处理后的下单金额:%s" % cash_amount) 525 | 526 | if float(cash_amount) < self.huobi_min_cash_amount: 527 | self.timeLog("金额:%s 小于交易所最小交易金额(火币最小金额:1元),因此无法下单,此处忽略该信号" % (cash_amount, self.huobi_min_cash_amount), 528 | level=logging.WARN) 529 | return None 530 | 531 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 532 | market = helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"] 533 | 534 | res = self.HuobiService.buyMarket(coin_type, cash_amount, None, None, market, BUY_MARKET) 535 | if helper.componentExtract(res, u"result", "") != u'success': 536 | self.timeLog("下达火币市价买单(金额:%s)失败, 结果是:%s!" % (cash_amount, helper.componentExtract(res, u"result", "")), 537 | level=logging.ERROR) 538 | return None 539 | order_id = res[u"id"] 540 | # 查询订单执行情况 541 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 542 | self.timeLog("下达如下火币市价买单,金额:%s" % cash_amount) 543 | self.timeLog(str(order_info)) 544 | 545 | retry_time = 0 546 | while retry_time < self.huobi_order_query_retry_maximum_times and order_info["status"] != 2: 547 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 548 | time.sleep(self.orderWaitingTime) 549 | order_info = self.HuobiService.getOrderInfo(coin_type, order_id, market, ORDER_INFO) 550 | self.timeLog(str(order_info)) 551 | retry_time += 1 552 | 553 | if float(order_info["processed_price"]) > 0: 554 | executed_qty = float(order_info["processed_amount"]) / float(order_info["processed_price"]) 555 | self.timeLog("火币市价买单已被执行,执行数量:%f,花费的现金:%.2f" % (executed_qty, float(order_info["processed_amount"]))) 556 | return executed_qty 557 | else: 558 | self.timeLog("火币市价买单未被执行", level=logging.WARN) 559 | return 0 560 | 561 | elif exchange == "okcoin": 562 | if sell_1_price is None: 563 | raise ValueError("处理okcoin市价买单之前,需要提供当前Okcoin卖一的价格信息,请检查传入的sell_1_price参数是否完备!") 564 | self.timeLog("开始下达okcoin市价买单...") 565 | self.timeLog("只保留下单数量的小数点后2位...") 566 | self.timeLog("原始下单金额:%s" % cash_amount) 567 | tmp = float(cash_amount) 568 | tmp = helper.downRound(tmp, 2) 569 | cash_amount = str(tmp) 570 | self.timeLog("做完小数点处理后的下单金额:%s" % cash_amount) 571 | 572 | if float(cash_amount) < self.okcoin_min_quantity * sell_1_price: 573 | self.timeLog( 574 | "金额:%s 不足以购买交易所最小交易数量(okcoin最小数量:%f,当前卖一价格%.2f,最小金额要求:%.2f),因此无法下单,此处忽略该信号" % ( 575 | cash_amount, self.okcoin_min_quantity, sell_1_price, self.okcoin_min_quantity * sell_1_price), 576 | level=logging.WARN) 577 | return None 578 | 579 | coin_type = helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"] 580 | res = self.OKCoinService.trade(coin_type, "buy_market", price=cash_amount) 581 | 582 | if helper.componentExtract(res, "result") != True: 583 | self.timeLog("下达okcoin市价买单(金额:%s)失败, 结果是:%s!" % (cash_amount, helper.componentExtract(res, "result")), 584 | level=logging.ERROR) 585 | return None 586 | order_id = res["order_id"] 587 | # 查询订单执行情况 588 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 589 | self.timeLog("下达如下okcoin市价买单,金额:%s" % cash_amount) 590 | self.timeLog(str(order_info)) 591 | 592 | retry_time = 0 593 | while retry_time < self.okcoin_order_query_retry_maximum_times and order_info["orders"][0]["status"] != 2: 594 | self.timeLog("等待%.1f秒直至订单完成" % self.orderWaitingTime) 595 | time.sleep(self.orderWaitingTime) 596 | order_info = self.OKCoinService.orderInfo(coin_type, str(order_id)) 597 | self.timeLog(str(order_info)) 598 | retry_time += 1 599 | 600 | executed_qty = order_info["orders"][0]["deal_amount"] 601 | self.timeLog("okcoin市价买单已被执行,执行数量:%f,花费的现金:%.2f" % ( 602 | executed_qty, executed_qty * order_info["orders"][0]["avg_price"])) 603 | return executed_qty 604 | 605 | # 再平衡仓位 606 | def rebalance_position(self, accountInfo, price): 607 | current_huobi_pos_value = accountInfo[ 608 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_str"]] * price 609 | target_huobi_pos_value = accountInfo["huobi_cny_net"] * self.rebalanced_position_proportion 610 | if target_huobi_pos_value - current_huobi_pos_value > self.spread_pos_qty_minimum_size * price: # need to buy 611 | self.timeLog("触发再平衡信号,增加huobi仓位", level=logging.WARN) 612 | self.buy_market(self.coinMarketType, str(target_huobi_pos_value - current_huobi_pos_value), 613 | exchange="huobi") 614 | elif current_huobi_pos_value - target_huobi_pos_value > self.spread_pos_qty_minimum_size * price: # need to sell 615 | self.timeLog("触发再平衡信号,减小huobi仓位", level=logging.WARN) 616 | self.sell_market(self.coinMarketType, str((current_huobi_pos_value - target_huobi_pos_value) / price), 617 | exchange="huobi") 618 | 619 | current_okcoin_pos_value = accountInfo[ 620 | helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_str"]] * price 621 | target_okcoin_pos_value = accountInfo["okcoin_cny_net"] * self.rebalanced_position_proportion 622 | if target_okcoin_pos_value - current_okcoin_pos_value > self.spread_pos_qty_minimum_size * price: # need to buy 623 | self.timeLog("触发再平衡信号,增加okcoin仓位", level=logging.WARN) 624 | self.buy_market(self.coinMarketType, str(target_okcoin_pos_value - current_okcoin_pos_value), 625 | exchange="okcoin", 626 | sell_1_price=price) 627 | elif current_okcoin_pos_value - target_okcoin_pos_value > self.spread_pos_qty_minimum_size * price: # need to sell 628 | self.timeLog("触发再平衡信号,减小okcoin仓位", level=logging.WARN) 629 | self.sell_market(self.coinMarketType, str((current_okcoin_pos_value - target_okcoin_pos_value) / price), 630 | exchange="okcoin") 631 | 632 | self.current_position_direction = 0 # 1: long spread1(buy okcoin, sell huobi); 2: long spread2( buy huobi, sell okcoin), 0: no position 633 | self.spread1_pos_qty = 0 634 | self.spread2_pos_qty = 0 635 | 636 | # 获取现在的持仓比例,取火币和okcoin的持仓比例、现金比例的最大值 637 | def get_current_position_proportion(self, accountInfo, price): 638 | huobi_net = accountInfo["huobi_cny_net"] 639 | huobi_pos_value = accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_str"]] * price 640 | huobi_cash_value = accountInfo["huobi_cny_cash"] 641 | okcoin_net = accountInfo["okcoin_cny_net"] 642 | okcoin_pos_value = accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_str"]] * price 643 | okcoin_cash_value = accountInfo["okcoin_cny_cash"] 644 | if huobi_net == 0: 645 | raise ValueError("您在火币的净资产为零,无法做价差套利,请上huobi.com充值!") 646 | if okcoin_net == 0: 647 | raise ValueError("您在OKCoin的净资产为零,无法做价差套利,请上okcoin.cn充值!") 648 | return np.max([huobi_pos_value / huobi_net, huobi_cash_value / huobi_net, okcoin_pos_value / okcoin_net, 649 | okcoin_cash_value / okcoin_net]) 650 | 651 | # 计算移动平均 652 | def calc_sma_and_deviation(self): 653 | self.spread1_mean = np.mean(self.spread1List[-1 * self.sma_window_size:]) 654 | self.spread1_stdev = np.std(self.spread1List[-1 * self.sma_window_size:]) 655 | self.spread2_mean = np.mean(self.spread2List[-1 * self.sma_window_size:]) 656 | self.spread2_stdev = np.std(self.spread2List[-1 * self.sma_window_size:]) 657 | 658 | # 判断开仓、平仓 659 | def in_or_out(self): 660 | if self.current_position_direction == 0: # currently no spread position 661 | if (self.spread1List[-1] - self.spread1_mean) / self.spread1_stdev > self.spread1_open_condition_stdev_coe: # huobi > okcoin 662 | return 1 # buy okcoin, sell huobi 663 | elif (self.spread2List[-1] - self.spread2_mean) / self.spread2_stdev > self.spread2_open_condition_stdev_coe: # okcoin > huobi 664 | return 2 # sell okcoin, buy huobi 665 | elif self.current_position_direction == 1: # currently long spread1 666 | if (self.spread1List[-1] - self.spread1_mean) / self.spread1_stdev > self.spread1_open_condition_stdev_coe: # huobi > okcoin 667 | return 1 # continue to buy okcoin, sell huobi, meaning upsizing spread1 668 | if (self.spread2List[-1] - self.spread2_mean) / self.spread2_stdev > -self.spread1_close_condition_stdev_coe: # spread1 close is using the price data of spread2, so check spread2 here 669 | return 2 # unwind spread1 670 | elif self.current_position_direction == 2: # currently long spread1 671 | if (self.spread2List[-1] - self.spread2_mean) / self.spread2_stdev > self.spread2_open_condition_stdev_coe: # okcoin > huobi 672 | return 2 # continue to sell okcoin, buy huobi, meaning upsizing spread2 673 | if (self.spread1List[-1] - self.spread1_mean) / self.spread1_stdev > -self.spread2_close_condition_stdev_coe: # spread2 close is using the price data of spread1, so check spread1 here 674 | return 1 # unwind spread2 675 | return 0 # no action 676 | 677 | # 向list添加1个元素,当list长度大于某个定值时,会将前面超出的部分删除 678 | def add_to_list(self, dest_list, element, max_length): 679 | if max_length == 1: 680 | return [element] 681 | while len(dest_list) >= max_length: 682 | del (dest_list[0]) 683 | dest_list.append(element) 684 | return dest_list 685 | 686 | # 主要的逻辑 687 | def go(self): 688 | self.timeLog("日志启动于 %s" % self.getStartRunningTime().strftime(self.TimeFormatForLog)) 689 | self.dataLog( 690 | content="time|huobi_cny_cash|huobi_cny_btc|huobi_cny_ltc|huobi_cny_cash_loan|huobi_cny_btc_loan|huobi_cny_ltc_loan|huobi_cny_cash_frozen|huobi_cny_btc_frozen|huobi_cny_ltc_frozen|huobi_cny_total|huobi_cny_net|okcoin_cny_cash|okcoin_cny_btc|okcoin_cny_ltc|okcoin_cny_cash_frozen|okcoin_cny_btc_frozen|okcoin_cny_ltc_frozen|okcoin_cny_total|okcoin_cny_net|total_net") 691 | self.dataLog() 692 | while True: 693 | if self.timeInterval > 0: 694 | self.timeLog("等待 %d 秒进入下一个循环..." % self.timeInterval) 695 | time.sleep(self.timeInterval) 696 | 697 | self.timeLog("记录心跳信息...") 698 | 699 | # calculate the net asset at a fixed time window 700 | time_diff = datetime.datetime.now() - self.last_data_log_time 701 | if time_diff.seconds > self.dataLogFixedTimeWindow: 702 | self.dataLog() 703 | 704 | # 查询huobi第一档深度数据 705 | huobiDepth = self.HuobiService.getDepth(helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"], 706 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 707 | depth_size=1) 708 | # 查询okcoin第一档深度数据 709 | okcoinDepth = self.OKCoinService.depth(helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_type"]) 710 | 711 | huobi_sell_1_price = huobiDepth["asks"][0][0] 712 | huobi_sell_1_qty = huobiDepth["asks"][0][1] 713 | huobi_buy_1_price = huobiDepth["bids"][0][0] 714 | huobi_buy_1_qty = huobiDepth["bids"][0][1] 715 | okcoin_sell_1_price = okcoinDepth["asks"][0][0] 716 | okcoin_sell_1_qty = okcoinDepth["asks"][0][1] 717 | okcoin_buy_1_price = okcoinDepth["bids"][0][0] 718 | okcoin_buy_1_qty = okcoinDepth["bids"][0][1] 719 | spread1 = huobi_buy_1_price - okcoin_sell_1_price 720 | spread2 = okcoin_buy_1_price - huobi_sell_1_price 721 | self.spread1List = self.add_to_list(self.spread1List, spread1, self.sma_window_size) 722 | self.spread2List = self.add_to_list(self.spread2List, spread2, self.sma_window_size) 723 | max_price = np.max([huobi_sell_1_price, huobi_buy_1_price, okcoin_sell_1_price, okcoin_buy_1_price]) 724 | 725 | if len(self.spread1List) < self.sma_window_size: 726 | self.timeLog("正在获取计算SMA所需的最小数据量(%d/%d)..." % (len(self.spread1List), self.sma_window_size)) 727 | continue 728 | 729 | # 获取当前账户信息 730 | accountInfo = self.getAccuntInfo() 731 | 732 | # check whether current time is after the dailyExitTime, if yes, exit 733 | if self.dailyExitTime is not None and datetime.datetime.now() > datetime.datetime.strptime( 734 | datetime.date.today().strftime("%Y-%m-%d") + " " + self.dailyExitTime, 735 | "%Y-%m-%d %H:%M:%S"): 736 | self.timeLog("抵达每日终结时间:%s, 现在退出." % self.dailyExitTime) 737 | if self.auto_rebalance_on_exit: 738 | self.rebalance_position(accountInfo, max_price) 739 | break 740 | 741 | current_position_proportion = self.get_current_position_proportion(accountInfo, max_price) 742 | 743 | self.calc_sma_and_deviation() 744 | 745 | if self.auto_rebalance_on and current_position_proportion > self.position_proportion_threshold: 746 | if abs(self.spread2List[ 747 | -1] - self.spread2_mean) / self.spread2_stdev < self.spread2_close_condition_stdev_coe or abs( 748 | self.spread1List[ 749 | -1] - self.spread1_mean) / self.spread1_stdev < self.spread1_close_condition_stdev_coe: 750 | self.rebalance_position(accountInfo, max_price) 751 | continue 752 | 753 | in_or_out = self.in_or_out() 754 | if in_or_out == 0: 755 | self.timeLog("没有发现交易信号,进入下一个轮询...") 756 | continue 757 | elif in_or_out == 1: 758 | if self.current_position_direction == 0: # 当前没有持仓 759 | self.timeLog("发现开仓信号(火币卖,OKcoin买)") 760 | elif self.current_position_direction == 1: # 当前long spread1 761 | self.timeLog("发现增仓信号(火币卖,OKcoin买)") 762 | elif self.current_position_direction == 2: # 当前long spread2 763 | self.timeLog("发现减仓信号(火币卖,OKcoin买)") 764 | 765 | self.timeLog("火币深度:%s" % str(huobiDepth)) 766 | self.timeLog("okcoin深度:%s" % str(okcoinDepth)) 767 | 768 | if self.current_position_direction == 0 or self.current_position_direction == 1: 769 | if current_position_proportion > self.position_proportion_alert_threshold: 770 | self.timeLog( 771 | "当前仓位比例:%f 大于仓位预警比例:%f" % ( 772 | current_position_proportion, self.position_proportion_alert_threshold), 773 | level=logging.WARN) 774 | # 每次只能吃掉一定ratio的深度 775 | Qty = helper.downRound(min(huobi_buy_1_qty, okcoin_sell_1_qty) * self.orderRatio, 4) 776 | elif self.current_position_direction == 2: 777 | depthQty = helper.downRound(min(huobi_buy_1_qty, okcoin_sell_1_qty) * self.orderRatio, 4) 778 | Qty = min(depthQty, helper.downRound(self.spread2_pos_qty, 4)) 779 | 780 | # 每次搬砖最多只搬maximum_qty_multiplier个最小单位 781 | if self.maximum_qty_multiplier is not None: 782 | Qty = min(Qty, 783 | max(self.huobi_min_quantity, self.okcoin_min_quantity) * self.maximum_qty_multiplier) 784 | 785 | # 每次搬砖前要检查是否有足够security和cash 786 | Qty = min(Qty, accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_str"]], 787 | helper.downRound(accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"][ 788 | "market_str"]] / okcoin_sell_1_price, 4)) 789 | Qty = helper.downRound(Qty, 4) 790 | Qty = helper.getRoundedQuantity(Qty, self.coinMarketType) 791 | 792 | if Qty < self.huobi_min_quantity or Qty < self.okcoin_min_quantity: 793 | self.timeLog( 794 | "数量:%f 小于交易所最小交易数量(火币最小数量:%f, okcoin最小数量:%f),因此无法下单并忽略该信号" % ( 795 | Qty, self.huobi_min_quantity, self.okcoin_min_quantity), level=logging.WARN) 796 | continue 797 | else: 798 | # step1: 先处理卖 799 | executed_qty = self.sell_market(self.coinMarketType, str(Qty), exchange="huobi") 800 | if executed_qty is not None: 801 | # step2: 再执行买 802 | Qty2 = min(executed_qty, Qty) 803 | Qty2 = max(helper.getRoundedQuantity(Qty2, self.coinMarketType), self.okcoin_min_quantity) 804 | 805 | if Qty2 < self.okcoin_min_quantity * 1.05: 806 | self.buy_market(self.coinMarketType, str(Qty2 * okcoin_sell_1_price * 1.05), 807 | exchange="okcoin", 808 | sell_1_price=okcoin_sell_1_price) 809 | else: 810 | self.buy_market(self.coinMarketType, str(Qty2 * okcoin_sell_1_price), exchange="okcoin", 811 | sell_1_price=okcoin_sell_1_price) 812 | 813 | if self.current_position_direction == 0 or self.current_position_direction == 1: 814 | self.spread1_pos_qty += Qty2 815 | elif self.current_position_direction == 2: 816 | self.spread2_pos_qty -= Qty2 817 | elif in_or_out == 2: 818 | if self.current_position_direction == 0: # 当前没有持仓 819 | self.timeLog("发现开仓信号(OKcoin卖, 火币买)") 820 | elif self.current_position_direction == 2: # 当前long spread2 821 | self.timeLog("发现增仓信号(OKcoin卖, 火币买)") 822 | elif self.current_position_direction == 1: # 当前long spread1 823 | self.timeLog("发现减仓信号(OKcoin卖, 火币买)") 824 | 825 | self.timeLog("火币深度:%s" % str(huobiDepth)) 826 | self.timeLog("okcoin深度:%s" % str(okcoinDepth)) 827 | 828 | if self.current_position_direction == 0 or self.current_position_direction == 2: 829 | if current_position_proportion > self.position_proportion_alert_threshold: 830 | self.timeLog( 831 | "当前仓位比例:%f 大于仓位预警比例:%f" % ( 832 | current_position_proportion, self.position_proportion_alert_threshold), 833 | level=logging.WARN) 834 | # 每次只能吃掉一定ratio的深度 835 | Qty = helper.downRound(min(huobi_sell_1_qty, okcoin_buy_1_qty) * self.orderRatio, 4) 836 | elif self.current_position_direction == 1: 837 | depthQty = helper.downRound(min(huobi_sell_1_qty, okcoin_buy_1_qty) * self.orderRatio, 4) 838 | Qty = min(depthQty, helper.downRound(self.spread1_pos_qty, 4)) 839 | 840 | # 每次搬砖最多只搬maximum_qty_multiplier个最小单位 841 | if self.maximum_qty_multiplier is not None: 842 | Qty = min(Qty, 843 | max(self.huobi_min_quantity, self.okcoin_min_quantity) * self.maximum_qty_multiplier) 844 | 845 | # 每次搬砖前要检查是否有足够security和cash 846 | Qty = min(Qty, accountInfo[helper.coinTypeStructure[self.coinMarketType]["okcoin"]["coin_str"]], 847 | helper.downRound(accountInfo[helper.coinTypeStructure[self.coinMarketType]["huobi"][ 848 | "market_str"]] / huobi_sell_1_price), 4) 849 | Qty = helper.getRoundedQuantity(Qty, self.coinMarketType) 850 | 851 | if Qty < self.huobi_min_quantity or Qty < self.okcoin_min_quantity: 852 | self.timeLog("数量:%f 小于交易所最小交易数量(火币最小数量:%f, okcoin最小数量:%f),因此无法下单并忽略该信号" % ( 853 | Qty, self.huobi_min_quantity, self.okcoin_min_quantity), level=logging.WARN) 854 | continue 855 | else: 856 | # step1: 先处理卖 857 | executed_qty = self.sell_market(self.coinMarketType, str(Qty), exchange="okcoin") 858 | if executed_qty is not None: 859 | # step2: 再执行买 860 | Qty2 = min(executed_qty, Qty) 861 | self.buy_market(self.coinMarketType, str(Qty2 * huobi_sell_1_price), exchange="huobi") 862 | if self.current_position_direction == 0 or self.current_position_direction == 2: 863 | self.spread2_pos_qty += Qty2 864 | elif self.current_position_direction == 1: 865 | self.spread1_pos_qty -= Qty2 866 | 867 | if self.spread1_pos_qty > self.spread_pos_qty_minimum_size: 868 | self.current_position_direction = 1 869 | elif self.spread2_pos_qty > self.spread_pos_qty_minimum_size: 870 | self.current_position_direction = 2 871 | else: 872 | self.current_position_direction = 0 873 | -------------------------------------------------------------------------------- /common/Account.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | class Account(object): 11 | pass 12 | -------------------------------------------------------------------------------- /common/Data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | class Data(object): 11 | pass 12 | -------------------------------------------------------------------------------- /common/Errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | class StartRunningTimeEmptyError(ValueError): 11 | pass 12 | 13 | 14 | class InvalidFilterError(ValueError): 15 | pass 16 | 17 | 18 | class TypeError(ValueError): 19 | pass 20 | 21 | 22 | class EmptySecurityError(ValueError): 23 | pass 24 | 25 | 26 | class InvalidSecurityError(ValueError): 27 | pass 28 | 29 | 30 | class InvalidFrequencyError(ValueError): 31 | pass 32 | -------------------------------------------------------------------------------- /common/Log.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | class WQLogger(object): 5 | def __init__(self, timeLogger): 6 | self.timeLogger = timeLogger 7 | 8 | def set_level(self, level): 9 | self.timeLogger.setLevel(level) 10 | 11 | def info(self, message): 12 | self.timeLogger.info(message) 13 | 14 | def warn(self, message): 15 | self.timeLogger.warn(message) 16 | 17 | def error(self, message): 18 | self.timeLogger.error(message) 19 | 20 | def debug(self, message): 21 | self.timeLogger.debug(message) 22 | -------------------------------------------------------------------------------- /common/Order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | class Order(object): 11 | pass 12 | -------------------------------------------------------------------------------- /common/Time.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | import datetime 11 | 12 | 13 | class Time(object): 14 | def __init__(self, startRunningTime): 15 | self.startRunningTime = startRunningTime 16 | 17 | def get_current_time(self): 18 | return datetime.datetime.now() 19 | 20 | def get_current_bar_time(self): 21 | # TODO:to implement the accurate get_current_bar_time function 22 | return self.get_current_time() 23 | 24 | def get_start_time(self): 25 | # TODO:to implement the accurate get_start_time function 26 | return self.startRunningTime 27 | 28 | def get_start_bar_time(self): 29 | # TODO:to implement the accurate get_start_bar_time function 30 | return self.get_start_time() 31 | -------------------------------------------------------------------------------- /common/UserData.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | class UserData(object): 11 | pass 12 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | -------------------------------------------------------------------------------- /data/dummy_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/data/dummy_file -------------------------------------------------------------------------------- /exchangeConnection/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/exchangeConnection/__init__.py -------------------------------------------------------------------------------- /exchangeConnection/bitvc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/exchangeConnection/bitvc/__init__.py -------------------------------------------------------------------------------- /exchangeConnection/bitvc/bitvcService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """BitVC api features & whatnot""" 5 | import hashlib 6 | import pprint 7 | 8 | import requests 9 | 10 | import accountConfig 11 | from exchangeConnection.bitvc.errors import error_text 12 | from utils.helper import * 13 | 14 | 15 | def config_map(): 16 | return { 17 | "key": accountConfig.BITVC["CNY_1"]["ACCESS_KEY"], 18 | "secret": accountConfig.BITVC["CNY_1"]["SECRET_KEY"], 19 | "base": accountConfig.BITVC["CNY_1"]["SERVICE_API"], 20 | "futurebase": accountConfig.BITVC["CNY_1"]["FUTURE_SERVICE_API"] 21 | } 22 | 23 | 24 | def format_check(output): 25 | """check for errors and print""" 26 | try: 27 | msg = error_text(output['code']) 28 | print("Error {}: {}".format(output['code'], msg)) 29 | except KeyError: 30 | ppt = pprint.PrettyPrinter(indent=4) 31 | ppt.pprint(output) 32 | 33 | 34 | class BitVC(object): 35 | """make requests, return data, and stuff""" 36 | 37 | def __init__(self): 38 | self.cfg = config_map() 39 | 40 | def sign(self, items): 41 | """ 42 | computes signed key to pass with authenticated request 43 | items: dict of parameters to include in auth request 44 | returns: tuple (md5 auth string, timestamp) 45 | """ 46 | auth = hashlib.md5() 47 | auth.update(("access_key=" + self.cfg['key'] + "&").encode('utf-8')) 48 | 49 | timestamp = int(time.time()) 50 | items["created"] = timestamp 51 | 52 | for key in sorted(items.keys()): 53 | auth.update((key + "=" + str(items[key]) + "&").encode('utf-8')) 54 | 55 | auth.update(("secret_key=" + self.cfg['secret']).encode('utf-8')) 56 | return (auth.hexdigest(), timestamp) 57 | 58 | def assets(self): 59 | """ 60 | get personal assets info 61 | returns: dict of balances 62 | """ 63 | sign = self.sign({}) 64 | params = {'access_key': self.cfg['key'], 'created': sign[1], 'sign': sign[0]} 65 | req = requests.post(self.cfg['base'] + 'accountInfo/get', data=params, timeout=20).json() 66 | 67 | return req 68 | 69 | def list_orders(self, currency): 70 | """ 71 | get list of orders 72 | currency: int 2 = Wright (fiat), 1 = BTC 73 | returns: list of order dicts 74 | """ 75 | sign = self.sign({'coin_type': currency}) 76 | params = {'access_key': self.cfg['key'], 'coin_type': currency, 'created': sign[1], 'sign': sign[0]} 77 | req = requests.post(self.cfg['base'] + 'order/list', data=params, timeout=20).json() 78 | 79 | return req 80 | 81 | def order_info(self, currency, order_id): 82 | """ 83 | get info on a specific order 84 | currency: int 2 = Wright (fiat), 1 = BTC 85 | order_id: int order id 86 | returns: dict with order info 87 | """ 88 | sign = self.sign({'coin_type': currency, 'id': order_id}) 89 | params = {'access_key': self.cfg['key'], 'coin_type': currency, 'created': sign[1], 'sign': sign[0]} 90 | req = requests.post(self.cfg['base'] + 'order/' + str(order_id), data=params, timeout=20).json() 91 | 92 | return req 93 | 94 | def order_cancel(self, currency, order_id): 95 | """ 96 | cancel order 97 | currency: int 2 = Wright (fiat), 1 = BTC 98 | order_id: int order id 99 | returns: dict, check 'Result' key 100 | """ 101 | sign = self.sign({'coin_type': currency, 'id': order_id}) 102 | params = {'access_key': self.cfg['key'], 'coin_type': currency, 'created': sign[1], 'sign': sign[0]} 103 | req = requests.post(self.cfg['base'] + 'cancel/' + str(order_id), data=params, timeout=20).json() 104 | 105 | return req 106 | 107 | 108 | class BitVCFuture(object): 109 | """make requests, return data, and stuff""" 110 | 111 | def __init__(self): 112 | self.cfg = config_map() 113 | 114 | def sign(self, items): 115 | """ 116 | computes signed key to pass with authenticated request 117 | items: dict of parameters to include in auth request 118 | returns: tuple (md5 auth string, timestamp) 119 | """ 120 | auth = hashlib.md5() 121 | auth.update(("accessKey=" + self.cfg['key'] + "&").encode('utf-8')) 122 | 123 | timestamp = int(time.time()) 124 | items["created"] = timestamp 125 | 126 | for key in sorted(items.keys()): 127 | auth.update((key + "=" + str(items[key]) + "&").encode('utf-8')) 128 | 129 | auth.update(("secretKey=" + self.cfg['secret']).encode('utf-8')) 130 | return (auth.hexdigest(), timestamp) 131 | 132 | def balance(self, coin_type): 133 | """ 134 | get personal assets info 135 | returns: dict of balances 136 | """ 137 | sign = self.sign({'coinType': coin_type}) 138 | params = {'accessKey': self.cfg['key'], 'coinType': coin_type, 'created': sign[1], 'sign': sign[0]} 139 | req = requests.post(self.cfg['futurebase'] + 'balance', data=params, timeout=20).json() 140 | 141 | return req 142 | 143 | def list_orders(self, currency): 144 | """ 145 | get list of orders 146 | currency: int 2 = Wright (fiat), 1 = BTC 147 | returns: list of order dicts 148 | """ 149 | sign = self.sign({'coin_type': currency}) 150 | params = {'access_key': self.cfg['key'], 'coin_type': currency, 'created': sign[1], 'sign': sign[0]} 151 | req = requests.post(self.cfg['futurebase'] + 'order/list', data=params, timeout=20).json() 152 | 153 | return req 154 | 155 | def order_info(self, currency, order_id): 156 | """ 157 | get info on a specific order 158 | currency: int 2 = Wright (fiat), 1 = BTC 159 | order_id: int order id 160 | returns: dict with order info 161 | """ 162 | sign = self.sign({'coin_type': currency, 'id': order_id}) 163 | params = {'access_key': self.cfg['key'], 'coin_type': currency, 'created': sign[1], 'sign': sign[0]} 164 | req = requests.post(self.cfg['futurebase'] + 'order/' + str(order_id), data=params, timeout=20).json() 165 | 166 | return req 167 | 168 | def order_cancel(self, currency, order_id): 169 | """ 170 | cancel order 171 | currency: int 2 = Wright (fiat), 1 = BTC 172 | order_id: int order id 173 | returns: dict, check 'Result' key 174 | """ 175 | sign = self.sign({'coin_type': currency, 'id': order_id}) 176 | params = {'access_key': self.cfg['key'], 'coin_type': currency, 'created': sign[1], 'sign': sign[0]} 177 | req = requests.post(self.cfg['futurebase'] + 'cancel/' + str(order_id), data=params, timeout=20).json() 178 | 179 | return req 180 | 181 | def get_current_bitvc_future_deal_price(self): 182 | req = requests.get("http://market.bitvc.com/futures/ticker_btc_week.js", timeout=20).json() 183 | return req 184 | 185 | 186 | # 返回期货平台的动态权益的人民币市值 187 | def getBitVCDynamicRightsInCNY(): 188 | bitvcFuture = BitVCFuture() 189 | if bitvcFuture.cfg["key"] == "": 190 | return 0 191 | else: 192 | balance = bitvcFuture.balance(HUOBI_COIN_TYPE_BTC) # 币种 1比特币 2莱特币 193 | dynamicRights = balance["dynamicRights"] 194 | priceStruct = bitvcFuture.get_current_bitvc_future_deal_price() 195 | return round(float(priceStruct["last"]) * dynamicRights, 2) 196 | -------------------------------------------------------------------------------- /exchangeConnection/bitvc/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """define bitvc errors""" 5 | 6 | 7 | # pylint: disable=C0301 8 | 9 | def error_text(error_num): 10 | """figure out our errors""" 11 | try: 12 | return ERRORS[error_num] 13 | except KeyError: 14 | return "Undefined Error" 15 | 16 | 17 | # used some vim-fu to help format this 18 | ERRORS = { 19 | 1: "Server Error", 20 | 2: "There is not enough yuan", 21 | 3: "Transaction has started, can not be started again", 22 | 4: "Transaction has ended", 23 | 10: "There is not enough bitcoins", 24 | 11: "Wright does not have enough coins", 25 | 18: "Wrong password funds", 26 | 26: "The commission does not exist", 27 | 32: "Insufficient amount, not transactions or withdrawals", 28 | 33: "The account is locked, you can not operate", 29 | 34: "Assets are locked, unable to trade", 30 | 35: "Leverage limits of your application has not yet returned, still can not buy coins Wright", 31 | 41: "The commission has ended, can not be modified", 32 | 42: "The commission has been canceled, can not be modified", 33 | 44: "Transaction price is too low", 34 | 45: "Transaction price is too high", 35 | 46: "The small number of transactions, the minimum number 0.001", 36 | 47: "Too much the number of transactions", 37 | 48: "Market orders can not be less than one yuan to buy the amount of", 38 | 49: "Limit orders can not be less than the number of transactions Bitcoin 0.001", 39 | 50: "110% of the purchase price can not be higher than the price of", 40 | 51: "Limit the number of single-Wright currency trading is not less than 0.01", 41 | 52: "The number can not be less than 0.001 sold", 42 | 53: "The number can not be less than 0.01 to sell", 43 | 54: "Selling price can not be less than 90% of the price of", 44 | 55: "105% of the purchase price can not be higher than the price of", 45 | 56: "Selling price can not be less than 95% of the price of", 46 | 64: "Invalid request", 47 | 65: "Ineffective methods", 48 | 66: "Access key validation fails", 49 | 67: "Private key authentication fails", 50 | 68: "Invalid price", 51 | 69: "Invalid quantity", 52 | 70: "Invalid submission time", 53 | 71: "Request too many times", 54 | 87: "The number of transactions is less than 0.1 BTC, please do not bid the price higher than the price of one percent", 55 | 88: "The number of transactions is less than 0.1 BTC, please do not sell below the market price of a 1%", 56 | 89: "The number of transactions is less than 0.1 LTC, please do not bid the price higher than the price of one percent", 57 | 90: "The number of transactions is less than 0.1 LTC, please do not sell below the market price of a 1%", 58 | 91: "Invalid currency", 59 | 92: "110% of the purchase price can not be higher than the price of", 60 | 93: "Selling price can not be less than 90% of the price of", 61 | 97: "You have to open financial transactions password, please submit financial transaction password parameters", 62 | 99: "Market orders can not be canceled", 63 | 110: "Bid prices can not be more than two decimal places", 64 | 111: "Bid quantity not more than four decimal places", 65 | 112: "Selling price can not be more than two decimal places", 66 | 113: "Can not sell more than four decimal number", 67 | 114: "Market orders to buy the amount can not be more than 1 million yuan", 68 | 115: "Bitcoin market order to sell the number of not less than 0.001", 69 | 116: "Bitcoin market orders to sell no more than the number 1000", 70 | 117: "Wright currency market order to sell no more than 100,000 the number of", 71 | 118: "Single failure", 72 | 119: "Wright currency market order to sell the number of not less than 0.01", 73 | 120: "Limit orders can not be more than the number of transactions Bitcoin 500", 74 | 121: "Limit order transaction price can not be less than 0.01", 75 | 122: "Limit orders can not be higher than the trading price of 100000", 76 | 125: "Limit orders can not be more than the number of transactions Wright currency 10000", 77 | } 78 | -------------------------------------------------------------------------------- /exchangeConnection/bitvc/testBitvc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from exchangeConnection.bitvc.bitvcService import * 5 | from utils.helper import * 6 | 7 | a = BitVC() 8 | print(a.assets()) 9 | print(a.list_orders(HUOBI_COIN_TYPE_BTC)) # 币种 1比特币 2莱特币 10 | 11 | b = BitVCFuture() 12 | balance = b.balance(HUOBI_COIN_TYPE_BTC) # 币种 1比特币 2莱特币 13 | dynamicRights = balance["dynamicRights"] 14 | priceStruct = b.get_current_bitvc_future_deal_price() 15 | dynamicRightsInRMB = float(priceStruct["last"]) * dynamicRights 16 | print(dynamicRightsInRMB) 17 | 18 | print(float(5.009)) 19 | 20 | print("%s" % float(5.009)) 21 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/exchangeConnection/huobi/__init__.py -------------------------------------------------------------------------------- /exchangeConnection/huobi/history.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import datetime 5 | import random 6 | 7 | import pandas as pd 8 | 9 | from utils.errors import * 10 | 11 | valid_security_list = ["huobi_cny_btc", "huobi_cny_ltc"] 12 | 13 | frequency_to_seconds = { 14 | "1m": 60, 15 | "5m": 5 * 60, 16 | "15m": 15 * 60, 17 | "30m": 30 * 60, 18 | "60m": 60 * 60, 19 | "1d": 60 * 60 * 24, 20 | "1w": 60 * 60 * 24 * 7, 21 | "1M": 60 * 60 * 24 * 30, 22 | "1y": 60 * 60 * 24 * 360 23 | } 24 | 25 | 26 | # TODO: 需要获取真实的历史数据,这里仅仅是一个Mock API 27 | # TODO: 需要加入current bar time 的check 28 | def get_current_price(security): 29 | # 取得最近1 minute bar的close 30 | minute_hist = get_price(security, count=1, frequency="1m") 31 | if len(minute_hist.index) < 1: 32 | raise ValueError("no valid 1 minute price") 33 | current_price = minute_hist['close'][-1] 34 | return current_price 35 | 36 | 37 | # TODO: 需要获取真实的历史数据,这里仅仅是一个Mock API 38 | # TODO: 需要加入current bar time 的check 39 | def get_price(security, count=None, start_bar_time=None, end_bar_time=None, frequency="5m"): 40 | if count is not None and start_bar_time is not None: 41 | raise InvalidFilterError 42 | 43 | if security not in valid_security_list: 44 | raise InvalidSecurityError 45 | 46 | result = {"bar_time": [], 47 | "security": [], 48 | "open": [], 49 | "high": [], 50 | "low": [], 51 | "close": [], 52 | "volume": []} 53 | 54 | if security == "huobi_cny_btc": 55 | base_price = 4000 56 | base_volume = 10 57 | std_dev = 30 58 | deviation = 3 59 | else: 60 | base_price = 25 61 | base_volume = 100 62 | std_dev = 5 63 | deviation = 3 64 | 65 | if count is not None: 66 | # 取end_bar_time之前(不包括end_bar_time)count根bar 67 | if end_bar_time is None: 68 | end_bar_time = datetime.datetime.now() 69 | last_record_time_stamp = end_bar_time 70 | open = base_price + random.uniform(-1 * std_dev * deviation, std_dev * deviation) 71 | close = base_price + random.uniform(-1 * std_dev * deviation, std_dev * deviation) 72 | high = max(open, close) + random.uniform(1, 2) 73 | low = min(open, close) - random.uniform(1, 2) 74 | volume = base_volume + random.uniform(-3, 3) 75 | result["bar_time"].append(last_record_time_stamp) 76 | result["security"].append(security) 77 | result["open"].append(open) 78 | result["high"].append(high) 79 | result["low"].append(low) 80 | result["close"].append(close) 81 | result["volume"].append(volume) 82 | last_time = last_record_time_stamp 83 | 84 | i = count 85 | while i > 0: 86 | bar_time = last_time - datetime.timedelta(seconds=frequency_to_seconds[frequency]) 87 | open = base_price + random.uniform(-1 * std_dev * deviation, std_dev * deviation) 88 | close = base_price + random.uniform(-1 * std_dev * deviation, std_dev * deviation) 89 | high = max(open, close) + random.uniform(1, 2) 90 | low = min(open, close) - random.uniform(1, 2) 91 | volume = base_volume + random.uniform(-3, 3) 92 | result["bar_time"].append(bar_time) 93 | result["security"].append(security) 94 | result["open"].append(open) 95 | result["high"].append(high) 96 | result["low"].append(low) 97 | result["close"].append(close) 98 | result["volume"].append(volume) 99 | last_time = bar_time 100 | i = i - 1 101 | 102 | result["bar_time"].reverse() 103 | result["security"].reverse() 104 | result["open"].reverse() 105 | result["high"].reverse() 106 | result["low"].reverse() 107 | result["close"].reverse() 108 | result["volume"].reverse() 109 | result = pd.DataFrame(result) 110 | result.index = result["bar_time"] 111 | return result 112 | else: 113 | bar_time = start_bar_time 114 | if end_bar_time is None: 115 | end_bar_time = datetime.datetime.now() 116 | while bar_time < end_bar_time: 117 | open = base_price + random.uniform(-1 * std_dev * deviation, std_dev * deviation) 118 | close = base_price + random.uniform(-1 * std_dev * deviation, std_dev * deviation) 119 | high = max(open, close) + random.uniform(1, 2) 120 | low = min(open, close) - random.uniform(1, 2) 121 | volume = base_volume + random.uniform(-3, 3) 122 | result["bar_time"].append(bar_time) 123 | result["security"].append(security) 124 | result["open"].append(open) 125 | result["high"].append(high) 126 | result["low"].append(low) 127 | result["close"].append(close) 128 | result["volume"].append(volume) 129 | bar_time = bar_time + datetime.timedelta(seconds=frequency_to_seconds[frequency]) 130 | result = pd.DataFrame(result) 131 | result.index = result["bar_time"] 132 | return result 133 | 134 | 135 | def get_all_securities(): 136 | result = {"security": [], 137 | "exchange": [], 138 | "settlement_currency": []} 139 | result["security"].append("huobi_cny_btc") 140 | result["exchange"].append("huobi") 141 | result["settlement_currency"].append("cny") 142 | 143 | result["security"].append("huobi_cny_ltc") 144 | result["exchange"].append("huobi") 145 | result["settlement_currency"].append("cny") 146 | result = pd.DataFrame(result) 147 | return result 148 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/huobiService.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from exchangeConnection.huobi.util import * 5 | from utils.helper import * 6 | 7 | ''' 8 | 获取账号详情 9 | ''' 10 | 11 | 12 | def getAccountInfo(market, method): 13 | params = {"method": method} 14 | extra = {} 15 | extra['market'] = market 16 | res = send2api(params, extra) 17 | return res 18 | 19 | 20 | ''' 21 | 获取所有正在进行的委托 22 | ''' 23 | 24 | 25 | def getOrders(coinType, market, method): 26 | params = {"method": method} 27 | params['coin_type'] = coinType 28 | extra = {} 29 | extra['market'] = market 30 | res = send2api(params, extra) 31 | return res 32 | 33 | 34 | ''' 35 | 获取订单详情 36 | @param coinType 37 | @param id 38 | ''' 39 | 40 | 41 | def getOrderInfo(coinType, id, market, method): 42 | params = {"method": method} 43 | params['coin_type'] = coinType 44 | params['id'] = id 45 | extra = {} 46 | extra['market'] = market 47 | res = send2api(params, extra) 48 | return res 49 | 50 | 51 | ''' 52 | 限价买入 53 | @param coinType 54 | @param price 55 | @param amount 56 | @param tradePassword 57 | @param tradeid 58 | @param method 59 | ''' 60 | 61 | 62 | def buy(coinType, price, amount, tradePassword, tradeid, market, method): 63 | params = {"method": method} 64 | params['coin_type'] = coinType 65 | params['price'] = price 66 | params['amount'] = amount 67 | extra = {} 68 | extra['trade_password'] = tradePassword 69 | extra['trade_id'] = tradeid 70 | extra['market'] = market 71 | res = send2api(params, extra) 72 | return res 73 | 74 | 75 | ''' 76 | 限价卖出 77 | @param coinType 78 | @param price 79 | @param amount 80 | @param tradePassword 81 | @param tradeid 82 | ''' 83 | 84 | 85 | def sell(coinType, price, amount, tradePassword, tradeid, market, method): 86 | params = {"method": method} 87 | params['coin_type'] = coinType 88 | params['price'] = price 89 | params['amount'] = amount 90 | extra = {} 91 | extra['trade_password'] = tradePassword 92 | extra['trade_id'] = tradeid 93 | extra['market'] = market 94 | res = send2api(params, extra) 95 | return res 96 | 97 | 98 | ''' 99 | 市价买 100 | @param coinType 101 | @param amount 102 | @param tradePassword 103 | @param tradeid 104 | ''' 105 | 106 | 107 | def buyMarket(coinType, amount, tradePassword, tradeid, market, method): 108 | params = {"method": method} 109 | params['coin_type'] = coinType 110 | params['amount'] = amount 111 | extra = {} 112 | extra['trade_password'] = tradePassword 113 | extra['trade_id'] = tradeid 114 | extra['market'] = market 115 | res = send2api(params, extra) 116 | return res 117 | 118 | 119 | ''' 120 | 市价卖出 121 | @param coinType 122 | @param amount 123 | @param tradePassword 124 | @param tradeid 125 | ''' 126 | 127 | 128 | def sellMarket(coinType, amount, tradePassword, tradeid, market, method): 129 | params = {"method": method} 130 | params['coin_type'] = coinType 131 | params['amount'] = amount 132 | extra = {} 133 | extra['trade_password'] = tradePassword 134 | extra['trade_id'] = tradeid 135 | extra['market'] = market 136 | res = send2api(params, extra) 137 | return res 138 | 139 | 140 | ''' 141 | 查询个人最新10条成交订单 142 | @param coinType 143 | ''' 144 | 145 | 146 | def getNewDealOrders(coinType, market, method): 147 | params = {"method": method} 148 | params['coin_type'] = coinType 149 | extra = {} 150 | extra['market'] = market 151 | res = send2api(params, extra) 152 | return res 153 | 154 | 155 | ''' 156 | 根据trade_id查询oder_id 157 | @param coinType 158 | @param tradeid 159 | ''' 160 | 161 | 162 | def getOrderIdByTradeId(coinType, tradeid, market, method): 163 | params = {"method": method} 164 | params['coin_type'] = coinType 165 | params['trade_id'] = tradeid 166 | extra = {} 167 | extra['market'] = market 168 | res = send2api(params, extra) 169 | return res 170 | 171 | 172 | ''' 173 | 撤销订单 174 | @param coinType 175 | @param id 176 | ''' 177 | 178 | 179 | def cancelOrder(coinType, id, market, method): 180 | params = {"method": method} 181 | params['coin_type'] = coinType 182 | params['id'] = id 183 | extra = {} 184 | extra['market'] = market 185 | res = send2api(params, extra) 186 | return res 187 | 188 | 189 | ''' 190 | 获取实时行情 191 | @param coinType #币种 1 比特币 2 莱特币 192 | ''' 193 | 194 | 195 | def getTicker(coinType, market): 196 | if market == COIN_TYPE_CNY: 197 | if coinType == HUOBI_COIN_TYPE_BTC: 198 | url = "http://api.huobi.com/staticmarket/ticker_btc_json.js" 199 | else: 200 | url = "http://api.huobi.com/staticmarket/ticker_ltc_json.js" 201 | elif market == COIN_TYPE_USD: 202 | if coinType == HUOBI_COIN_TYPE_BTC: 203 | url = "http://api.huobi.com/usdmarket/ticker_btc_json.js" 204 | else: 205 | raise ValueError("invalid coinType %d for market %s" % (coinType, market)) 206 | else: 207 | raise ValueError("invalid market %s" % market) 208 | return httpRequest(url, {}) 209 | 210 | 211 | ''' 212 | 获取实时行情 213 | @param coinType:币种 1 比特币 2 莱特币 214 | @param depth_size:指定深度 215 | ''' 216 | 217 | 218 | def getDepth(coinType, market, depth_size=5): 219 | if market == COIN_TYPE_CNY: 220 | if coinType == HUOBI_COIN_TYPE_BTC: 221 | url = "http://api.huobi.com/staticmarket/depth_btc_" + str(depth_size) + ".js" 222 | else: 223 | url = "http://api.huobi.com/staticmarket/depth_ltc_" + str(depth_size) + ".js" 224 | elif market == COIN_TYPE_USD: 225 | if coinType == HUOBI_COIN_TYPE_BTC: 226 | url = "http://api.huobi.com/usdmarket/depth_btc_" + str(depth_size) + ".js" 227 | else: 228 | raise ValueError("invalid coinType %d for market %s" % (coinType, market)) 229 | else: 230 | raise ValueError("invalid market %s" % market) 231 | return httpRequest(url, {}) 232 | 233 | 234 | ''' 235 | 获取最少订单数量 236 | @param coinType:币种 1 比特币 2 莱特币 237 | 火币上比特币交易及莱特币交易都是0.0001的整数倍 238 | 比特币最小交易数量:0.001,莱特币最小交易数量:0.01 239 | ''' 240 | 241 | 242 | def getMinimumOrderQty(coinType): 243 | if coinType == HUOBI_COIN_TYPE_BTC: 244 | return 0.001 245 | else: 246 | return 0.01 247 | 248 | 249 | ''' 250 | 获取最少交易金额 251 | 火币上比特币交易及莱特币交易金额都是0.01的整数倍 252 | 最小交易金额:1 253 | ''' 254 | 255 | 256 | def getMinimumOrderCashAmount(): 257 | return 1 258 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/huobiServiceETH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Date : 2017-05-16 15:40:03 4 | # @Author : Ryan (tech@huobi.com) 5 | # @Link : https://www.huobi.com 6 | # @Version : $Id$ 7 | 8 | from utilETH import * 9 | 10 | ''' 11 | Market data API 12 | ''' 13 | 14 | 15 | # 获取KLine 16 | def get_kline(symbol, period, long_polling=None): 17 | """ 18 | :param symbol: 可选值:{ ethcny } 19 | :param period: 可选值:{1min, 5min, 15min, 30min, 60min, 1day, 1mon, 1week, 1year } 20 | :param long_polling: 可选值: { true, false } 21 | :return: 22 | """ 23 | params = {'symbol': symbol, 24 | 'period': period} 25 | 26 | if long_polling: 27 | params['long-polling'] = long_polling 28 | url = MARKET_URL + '/market/kline' 29 | return http_get_request(url, params) 30 | 31 | 32 | # 获取marketdepth 33 | def get_depth(symbol, type, long_polling=None): 34 | """ 35 | :param symbol: 可选值:{ ethcny } 36 | :param type: 可选值:{ percent10, step0, step1, step2, step3, step4, step5 } 37 | :param long_polling: 可选值: { true, false } 38 | :return: 39 | """ 40 | params = {'symbol': symbol, 41 | 'type': type} 42 | 43 | if long_polling: 44 | params['long-polling'] = long_polling 45 | url = MARKET_URL + '/market/depth' 46 | return http_get_request(url, params) 47 | 48 | 49 | # 获取tradedetail 50 | def get_trade(symbol, long_polling=None): 51 | """ 52 | :param symbol: 可选值:{ ethcny } 53 | :param long_polling: 可选值: { true, false } 54 | :return: 55 | """ 56 | params = {'symbol': symbol} 57 | if long_polling: 58 | params['long-polling'] = long_polling 59 | url = MARKET_URL + '/market/trade' 60 | return http_get_request(url, params) 61 | 62 | 63 | # 获取 Market Detail 24小时成交量数据 64 | def get_detail(symbol, long_polling=None): 65 | """ 66 | :param symbol: 可选值:{ ethcny } 67 | :param long_polling: 可选值: { true, false } 68 | :return: 69 | """ 70 | params = {'symbol': symbol} 71 | if long_polling: 72 | params['long-polling'] = long_polling 73 | url = MARKET_URL + '/market/detail' 74 | return http_get_request(url, params) 75 | 76 | ''' 77 | Trade/Account API 78 | ''' 79 | 80 | 81 | def get_accounts(): 82 | """ 83 | :return: 84 | """ 85 | path = "/v1/account/accounts" 86 | params = {} 87 | return api_key_get(params, path) 88 | 89 | 90 | # 获取当前账户资产 91 | def get_balance(acct_id=None): 92 | """ 93 | :param acct_id 94 | :return: 95 | """ 96 | 97 | if not acct_id: 98 | accounts = get_accounts() 99 | acct_id = accounts['data'][0]['id']; 100 | 101 | url = "/v1/account/accounts/{0}/balance".format(acct_id) 102 | params = {"account-id": acct_id} 103 | return api_key_get(params, url) 104 | 105 | 106 | # 下单 107 | def orders(amount, source, symbol, _type, price=0): 108 | """ 109 | 110 | :param amount: 111 | :param source: 112 | :param symbol: 113 | :param _type: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖} 114 | :param price: 115 | :return: 116 | """ 117 | accounts = get_accounts() 118 | acct_id = accounts['data'][0]['id']; 119 | 120 | params = {"account-id": acct_id, 121 | "amount": amount, 122 | "symbol": symbol, 123 | "type": _type, 124 | "source": source} 125 | if price: 126 | params["price"] = price 127 | 128 | url = "/v1/order/orders" 129 | return api_key_post(params, url) 130 | 131 | 132 | # 执行订单 133 | def place_order(order_id): 134 | """ 135 | 136 | :param order_id: 137 | :return: 138 | """ 139 | params = {} 140 | url = "/v1/order/orders/{0}/place".format(order_id) 141 | return api_key_post(params, url) 142 | 143 | 144 | # 撤销订单 145 | def cancel_order(order_id): 146 | """ 147 | 148 | :param order_id: 149 | :return: 150 | """ 151 | params = {} 152 | url = "/v1/order/orders/{0}/submitcancel".format(order_id) 153 | return api_key_post(params, url) 154 | 155 | 156 | # 查询某个订单 157 | def order_info(order_id): 158 | """ 159 | 160 | :param order_id: 161 | :return: 162 | """ 163 | params = {} 164 | url = "/v1/order/orders/{0}".format(order_id) 165 | return api_key_get(params, url) 166 | 167 | 168 | # 查询某个订单的成交明细 169 | def order_matchresults(order_id): 170 | """ 171 | 172 | :param order_id: 173 | :return: 174 | """ 175 | params = {} 176 | url = "/v1/order/orders/{0}/matchresults".format(order_id) 177 | return api_key_get(params, url) 178 | 179 | 180 | # 查询当前委托、历史委托 181 | def orders_list(symbol, states, types=None, start_date=None, end_date=None, _from=None, direct=None, size=None): 182 | """ 183 | 184 | :param symbol: 185 | :param states: 可选值 {pre-submitted 准备提交, submitted 已提交, partial-filled 部分成交, partial-canceled 部分成交撤销, filled 完全成交, canceled 已撤销} 186 | :param types: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖} 187 | :param start_date: 188 | :param end_date: 189 | :param _from: 190 | :param direct: 可选值{prev 向前,next 向后} 191 | :param size: 192 | :return: 193 | """ 194 | params = {'symbol': symbol, 195 | 'states': states} 196 | 197 | if types: 198 | params[types] = types 199 | if start_date: 200 | params['start-date'] = start_date 201 | if end_date: 202 | params['end-date'] = end_date 203 | if _from: 204 | params['from'] = _from 205 | if direct: 206 | params['direct'] = direct 207 | if size: 208 | params['size'] = size 209 | url = '/v1/order/orders' 210 | return api_key_get(params, url) 211 | 212 | 213 | # 查询当前成交、历史成交 214 | def orders_matchresults(symbol, types=None, start_date=None, end_date=None, _from=None, direct=None, size=None): 215 | """ 216 | 217 | :param symbol: 218 | :param types: 可选值 {buy-market:市价买, sell-market:市价卖, buy-limit:限价买, sell-limit:限价卖} 219 | :param start_date: 220 | :param end_date: 221 | :param _from: 222 | :param direct: 可选值{prev 向前,next 向后} 223 | :param size: 224 | :return: 225 | """ 226 | params = {'symbol': symbol} 227 | 228 | if types: 229 | params[types] = types 230 | if start_date: 231 | params['start-date'] = start_date 232 | if end_date: 233 | params['end-date'] = end_date 234 | if _from: 235 | params['from'] = _from 236 | if direct: 237 | params['direct'] = direct 238 | if size: 239 | params['size'] = size 240 | url = '/v1/order/matchresults' 241 | return api_key_get(params, url) 242 | 243 | 244 | # 查询虚拟币提现地址 245 | def get_withdraw_address(currency): 246 | """ 247 | 248 | :param currency: 249 | :return: 250 | """ 251 | params = {'currency': currency} 252 | url = '/v1/dw/withdraw-virtual/addresses' 253 | return api_key_get(params, url) 254 | 255 | 256 | # 申请提现虚拟币 257 | def withdraw(address_id, amount): 258 | """ 259 | 260 | :param address_id: 261 | :param amount: 262 | :return: 263 | """ 264 | params = {'address-id': address_id, 265 | 'amount': amount} 266 | url = '/v1/dw/withdraw-virtual/create' 267 | return api_key_post(params, url) 268 | 269 | 270 | # 确认申请虚拟币提现 271 | def place_withdraw(address_id): 272 | """ 273 | 274 | :param address_id: 275 | :return: 276 | """ 277 | params = {} 278 | url = '/v1/dw/withdraw-virtual/{0}/place'.format(address_id) 279 | return api_key_post(params, url) 280 | 281 | 282 | # 申请取消提现虚拟币 283 | def cancel_withdraw(address_id): 284 | """ 285 | 286 | :param address_id: 287 | :return: 288 | """ 289 | params = {} 290 | url = '/v1/dw/withdraw-virtual/{0}/cancel'.format(address_id) 291 | return api_key_post(params, url) 292 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/testHuobi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ''' 5 | 本程序在 Python 3.3.0 环境下测试成功 6 | 使用方法:python testHuobi.py 7 | ''' 8 | 9 | import exchangeConnection.huobi.huobiService as HuobiService 10 | from exchangeConnection.huobi.util import * 11 | from utils.helper import * 12 | 13 | if __name__ == "__main__": 14 | print("获取账号详情") 15 | print(HuobiService.getAccountInfo("cny", ACCOUNT_INFO)) 16 | 17 | print(HuobiService.getAccountInfo("usd", ACCOUNT_INFO)) 18 | 19 | print("real time market") 20 | t = HuobiService.getTicker(HUOBI_COIN_TYPE_BTC, "cny") 21 | print(t) 22 | # t.json() 23 | # print (t.ticker.buy) 24 | # print (t['ticker']['sell']) 25 | 26 | print("get depth") 27 | print(HuobiService.getDepth(HUOBI_COIN_TYPE_BTC, "cny", depth_size=1)) 28 | 29 | print('order execution status') 30 | print(HuobiService.getOrderInfo(HUOBI_COIN_TYPE_LTC, 326711967, "cny", ORDER_INFO)) 31 | 32 | ''' 33 | print ("获取所有正在进行的委托") 34 | print (HuobiService.getOrders(1,GET_ORDERS)) 35 | print ("获取订单详情") 36 | print (HuobiService.getOrderInfo(1,68278313,ORDER_INFO)) 37 | print ("限价买入") 38 | print (HuobiService.buy(1,"1","0.01",None,None,BUY)) 39 | print ("限价卖出") 40 | print (HuobiService.sell(2,"100","0.2",None,None,SELL)) 41 | print ("市价买入") 42 | print (HuobiService.buyMarket(2,"30",None,None,BUY_MARKET)) 43 | print ("市价卖出") 44 | print (HuobiService.sellMarket(2,"1.3452",None,None,SELL_MARKET)) 45 | print ("查询个人最新10条成交订单") 46 | print (HuobiService.getNewDealOrders(1,NEW_DEAL_ORDERS)) 47 | print ("根据trade_id查询order_id") 48 | print (HuobiService.getOrderIdByTradeId(1,274424,ORDER_ID_BY_TRADE_ID)) 49 | print ("取消订单接口") 50 | print (HuobiService.cancelOrder(1,68278313,CANCEL_ORDER)) 51 | ''' 52 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/testHuobiETH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Date : 2017-05-16 15:29:19 4 | # @Author : Ryan (tech@huobi.com) 5 | # @Link : https://www.huobi.com 6 | # @Version : $Id$ 7 | 8 | from utilETH import * 9 | import huobiServiceETH 10 | 11 | if __name__ == '__main__': 12 | # print("获取1分钟线") 13 | # print(huobiServiceETH.get_kline('ethcny', '1min')) 14 | # print("获取合并深度为1的盘口") 15 | # print(huobiServiceETH.get_depth('ethcny', 'step1')) 16 | # print("获取Trade Detail") 17 | # print(huobiServiceETH.get_trade('ethcny')) 18 | # print("获取 Market Detail 24小时成交量数据") 19 | # print(huobiServiceETH.get_detail('ethcny')) 20 | print("获取当前账户资产") 21 | print(huobiServiceETH.get_balance()) 22 | # print('下单') 23 | # print(huobiServiceETH.orders(1, 'api', 'ethcny', 'buy-limit', 12)) 24 | # print('执行订单') 25 | # print(huobiServiceETH.place_order('58840')) 26 | # print('撤销订单') 27 | # print(huobiServiceETH.cancel_order('58840')) 28 | # print('查询某个订单') 29 | # print(huobiServiceETH.order_info('58840')) 30 | # print('查询某个订单的成交明细') 31 | # print(huobiServiceETH.order_matchresults('45421')) 32 | # print('查询当前委托、历史委托') 33 | # print(huobiServiceETH.orders_list('ethcny', 'submitted')) 34 | # print('查询当前成交、历史成交') 35 | # print(huobiServiceETH.orders_matchresults('ethcny')) 36 | # print('查询虚拟币提币地址') 37 | # print(huobiServiceETH.get_withdraw_address('eth')) 38 | 39 | 40 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import hashlib 5 | import time 6 | import urllib 7 | import urllib.parse 8 | import urllib.request 9 | 10 | import requests 11 | 12 | import accountConfig 13 | 14 | # 在此输入您的Key 15 | ACCESS_KEY = accountConfig.HUOBI["CNY_1"]["ACCESS_KEY"] 16 | SECRET_KEY = accountConfig.HUOBI["CNY_1"]["SECRET_KEY"] 17 | SERVICE_API = accountConfig.HUOBI["CNY_1"]["SERVICE_API"] 18 | 19 | ACCOUNT_INFO = "get_account_info" 20 | GET_ORDERS = "get_orders" 21 | ORDER_INFO = "order_info" 22 | BUY = "buy" 23 | BUY_MARKET = "buy_market" 24 | CANCEL_ORDER = "cancel_order" 25 | NEW_DEAL_ORDERS = "get_new_deal_orders" 26 | ORDER_ID_BY_TRADE_ID = "get_order_id_by_trade_id" 27 | SELL = "sell" 28 | SELL_MARKET = "sell_market" 29 | 30 | ''' 31 | 发送信息到api 32 | ''' 33 | 34 | 35 | def send2api(pParams, extra): 36 | pParams['access_key'] = ACCESS_KEY 37 | pParams['created'] = int(time.time()) 38 | pParams['sign'] = createSign(pParams) 39 | if (extra): 40 | for k in extra: 41 | v = extra.get(k) 42 | if (v != None): 43 | pParams[k] = v 44 | # pParams.update(extra) 45 | return httpRequest(SERVICE_API, pParams) 46 | 47 | 48 | ''' 49 | 生成签名 50 | ''' 51 | 52 | 53 | def createSign(params): 54 | params['secret_key'] = SECRET_KEY; 55 | params = sorted(params.items(), key=lambda d: d[0], reverse=False) 56 | message = urllib.parse.urlencode(params) 57 | message = message.encode(encoding='UTF8') 58 | m = hashlib.md5() 59 | m.update(message) 60 | m.digest() 61 | sig = m.hexdigest() 62 | return sig 63 | 64 | 65 | ''' 66 | request 67 | ''' 68 | 69 | 70 | def httpRequest(url, params): 71 | ''' 72 | postdata = urllib.parse.urlencode(params) 73 | postdata = postdata.encode('utf-8') 74 | 75 | fp = urllib.request.urlopen(url, postdata, timeout = 20) 76 | if fp.status != 200: 77 | return None 78 | else: 79 | mybytes = fp.read() 80 | mystr = mybytes.decode("utf8") 81 | fp.close() 82 | return mystr 83 | ''' 84 | headers = { 85 | "Content-type": "application/x-www-form-urlencoded", 86 | } 87 | 88 | postdata = urllib.parse.urlencode(params) 89 | # postdata = postdata.encode('utf-8') 90 | response = requests.post(url, postdata, headers=headers, timeout=20) 91 | if response.status_code == 200: 92 | return response.json() 93 | else: 94 | raise Exception("httpPost failed, detail is:%s" % response.text) 95 | -------------------------------------------------------------------------------- /exchangeConnection/huobi/utilETH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Date : 2017-05-16 15:40:04 4 | # @Author : Ryan (tech@huobi.com) 5 | # @Link : https://www.huobi.com 6 | # @Version : $Id$ 7 | # thanks to: KlausQiu 8 | 9 | import base64 10 | import datetime 11 | import hashlib 12 | import hmac 13 | import json 14 | import urllib 15 | import urllib.parse 16 | import urllib.request 17 | import requests 18 | 19 | # 此处填写APIKEY 20 | 21 | ACCESS_KEY = "" 22 | SECRET_KEY = "" 23 | 24 | 25 | # API 请求地址 26 | MARKET_URL = "https://be.huobi.com" 27 | TRADE_URL = "https://be.huobi.com" 28 | 29 | 30 | def http_get_request(url, params, add_to_headers=None): 31 | headers = { 32 | "Content-type": "application/x-www-form-urlencoded", 33 | } 34 | if add_to_headers: 35 | headers.update(add_to_headers) 36 | postdata = urllib.parse.urlencode(params) 37 | 38 | try: 39 | response = requests.get(url, postdata, headers=headers, timeout=5) 40 | if response.status_code == 200: 41 | return response.json() 42 | else: 43 | return 44 | except: 45 | print("httpGet failed, detail is:%s" % response.text) 46 | return 47 | 48 | 49 | def http_post_request(url, params, add_to_headers=None): 50 | headers = { 51 | "Accept": "application/json", 52 | 'Content-Type': 'application/json' 53 | } 54 | if add_to_headers: 55 | headers.update(add_to_headers) 56 | postdata = json.dumps(params) 57 | try: 58 | response = requests.post(url, postdata, headers=headers, timeout=10) 59 | if response.status_code == 200: 60 | return response.json() 61 | else: 62 | return 63 | except: 64 | print("httpPost failed, detail is:%s" % response.text) 65 | return 66 | 67 | 68 | def api_key_get(params, request_path): 69 | method = 'GET' 70 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') 71 | params.update({'AccessKeyId': ACCESS_KEY, 72 | 'SignatureMethod': 'HmacSHA256', 73 | 'SignatureVersion': '2', 74 | 'Timestamp': timestamp}) 75 | 76 | host_url = TRADE_URL 77 | host_name = urllib.parse.urlparse(host_url).hostname 78 | host_name = host_name.lower() 79 | params['Signature'] = createSign(params, method, host_name, request_path, SECRET_KEY) 80 | url = host_url + request_path 81 | return http_get_request(url, params) 82 | 83 | 84 | def api_key_post(params, request_path): 85 | method = 'POST' 86 | timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') 87 | params_to_sign = {'AccessKeyId': ACCESS_KEY, 88 | 'SignatureMethod': 'HmacSHA256', 89 | 'SignatureVersion': '2', 90 | 'Timestamp': timestamp} 91 | 92 | host_url = TRADE_URL 93 | host_name = urllib.parse.urlparse(host_url).hostname 94 | host_name = host_name.lower() 95 | params_to_sign['Signature'] = createSign(params_to_sign, method, host_name, request_path, SECRET_KEY) 96 | url = host_url + request_path + '?' + urllib.parse.urlencode(params_to_sign) 97 | return http_post_request(url, params) 98 | 99 | 100 | def createSign(pParams, method, host_url, request_path, secret_key): 101 | sorted_params = sorted(pParams.items(), key=lambda d: d[0], reverse=False) 102 | encode_params = urllib.parse.urlencode(sorted_params) 103 | payload = [method, host_url, request_path, encode_params] 104 | payload = '\n'.join(payload) 105 | payload = payload.encode(encoding='UTF8') 106 | secret_key = secret_key.encode(encoding='UTF8') 107 | digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest() 108 | signature = base64.b64encode(digest) 109 | signature = signature.decode() 110 | return signature 111 | 112 | -------------------------------------------------------------------------------- /exchangeConnection/okcoin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/exchangeConnection/okcoin/__init__.py -------------------------------------------------------------------------------- /exchangeConnection/okcoin/httpMD5Util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 用于进行http请求,以及MD5加密,生成签名的工具类 5 | 6 | import hashlib 7 | import urllib 8 | from urllib.parse import urljoin 9 | 10 | import requests 11 | 12 | 13 | def buildMySign(params, secretKey): 14 | sign = '' 15 | for key in sorted(params.keys()): 16 | sign += key + '=' + str(params[key]) + '&' 17 | data = sign + 'secret_key=' + secretKey 18 | return hashlib.md5(data.encode("utf8")).hexdigest().upper() 19 | 20 | 21 | def httpGet(url, resource, params=''): 22 | ''' 23 | conn = http.client.HTTPSConnection(url, timeout=10) 24 | conn.request("GET", resource + '?' + params) 25 | response = conn.getresponse() 26 | data = response.read().decode('utf-8') 27 | return json.loads(data) 28 | ''' 29 | fullURL = urljoin(url, resource + "?" + params) 30 | response = requests.get(fullURL, timeout=20) 31 | if response.status_code == 200: 32 | return response.json() 33 | else: 34 | raise Exception("httpGet failed, detail is:%s" % response.text) 35 | 36 | 37 | def httpPost(url, resource, params): 38 | ''' 39 | headers = { 40 | "Content-type": "application/x-www-form-urlencoded", 41 | } 42 | conn = http.client.HTTPSConnection(url, timeout=10) 43 | temp_params = urllib.parse.urlencode(params) 44 | conn.request("POST", resource, temp_params, headers) 45 | response = conn.getresponse() 46 | data = response.read().decode('utf-8') 47 | params.clear() 48 | conn.close() 49 | return json.loads(data) 50 | ''' 51 | headers = { 52 | "Content-type": "application/x-www-form-urlencoded", 53 | } 54 | fullURL = urljoin(url, resource) 55 | temp_params = urllib.parse.urlencode(params) 56 | response = requests.post(fullURL, temp_params, headers=headers, timeout=20) 57 | if response.status_code == 200: 58 | return response.json() 59 | else: 60 | raise Exception("httpPost failed, detail is:%s" % response.text) 61 | -------------------------------------------------------------------------------- /exchangeConnection/okcoin/okcoinFutureAPI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 用于访问OKCOIN 期货REST API 5 | from exchangeConnection.okcoin.httpMD5Util import buildMySign, httpGet, httpPost 6 | 7 | 8 | class OKCoinFuture: 9 | def __init__(self, url, apikey, secretkey): 10 | self.__url = url 11 | self.__apikey = apikey 12 | self.__secretkey = secretkey 13 | 14 | # OKCOIN期货行情信息 15 | def future_ticker(self, symbol, contractType): 16 | FUTURE_TICKER_RESOURCE = "/api/v1/future_ticker.do" 17 | params = '' 18 | if symbol: 19 | params += '&symbol=' + symbol if params else 'symbol=' + symbol 20 | if contractType: 21 | params += '&contract_type=' + contractType if params else 'contract_type=' + symbol 22 | return httpGet(self.__url, FUTURE_TICKER_RESOURCE, params) 23 | 24 | # OKCoin期货市场深度信息 25 | def future_depth(self, symbol, contractType, size): 26 | FUTURE_DEPTH_RESOURCE = "/api/v1/future_depth.do" 27 | params = '' 28 | if symbol: 29 | params += '&symbol=' + symbol if params else 'symbol=' + symbol 30 | if contractType: 31 | params += '&contract_type=' + contractType if params else 'contract_type=' + symbol 32 | if size: 33 | params += '&size=' + size if params else 'size=' + size 34 | return httpGet(self.__url, FUTURE_DEPTH_RESOURCE, params) 35 | 36 | # OKCoin期货交易记录信息 37 | def future_trades(self, symbol, contractType): 38 | FUTURE_TRADES_RESOURCE = "/api/v1/future_trades.do" 39 | params = '' 40 | if symbol: 41 | params += '&symbol=' + symbol if params else 'symbol=' + symbol 42 | if contractType: 43 | params += '&contract_type=' + contractType if params else 'contract_type=' + symbol 44 | return httpGet(self.__url, FUTURE_TRADES_RESOURCE, params) 45 | 46 | # OKCoin期货指数 47 | def future_index(self, symbol): 48 | FUTURE_INDEX = "/api/v1/future_index.do" 49 | params = '' 50 | if symbol: 51 | params = 'symbol=' + symbol 52 | return httpGet(self.__url, FUTURE_INDEX, params) 53 | 54 | # 获取美元人民币汇率 55 | def exchange_rate(self): 56 | EXCHANGE_RATE = "/api/v1/exchange_rate.do" 57 | return httpGet(self.__url, EXCHANGE_RATE, '') 58 | 59 | # 获取预估交割价 60 | def future_estimated_price(self, symbol): 61 | FUTURE_ESTIMATED_PRICE = "/api/v1/future_estimated_price.do" 62 | params = '' 63 | if symbol: 64 | params = 'symbol=' + symbol 65 | return httpGet(self.__url, FUTURE_ESTIMATED_PRICE, params) 66 | 67 | # 期货全仓账户信息 68 | def future_userinfo(self): 69 | FUTURE_USERINFO = "/api/v1/future_userinfo.do?" 70 | params = {} 71 | params['api_key'] = self.__apikey 72 | params['sign'] = buildMySign(params, self.__secretkey) 73 | return httpPost(self.__url, FUTURE_USERINFO, params) 74 | 75 | # 期货全仓持仓信息 76 | def future_position(self, symbol, contractType): 77 | FUTURE_POSITION = "/api/v1/future_position.do?" 78 | params = { 79 | 'api_key': self.__apikey, 80 | 'symbol': symbol, 81 | 'contract_type': contractType 82 | } 83 | params['sign'] = buildMySign(params, self.__secretkey) 84 | return httpPost(self.__url, FUTURE_POSITION, params) 85 | 86 | # 期货下单 87 | def future_trade(self, symbol, contractType, price='', amount='', tradeType='', matchPrice='', leverRate=''): 88 | FUTURE_TRADE = "/api/v1/future_trade.do?" 89 | params = { 90 | 'api_key': self.__apikey, 91 | 'symbol': symbol, 92 | 'contract_type': contractType, 93 | 'amount': amount, 94 | 'type': tradeType, 95 | 'match_price': matchPrice, 96 | 'lever_rate': leverRate 97 | } 98 | if price: 99 | params['price'] = price 100 | params['sign'] = buildMySign(params, self.__secretkey) 101 | return httpPost(self.__url, FUTURE_TRADE, params) 102 | 103 | # 期货批量下单 104 | def future_batchTrade(self, symbol, contractType, orders_data, leverRate): 105 | FUTURE_BATCH_TRADE = "/api/v1/future_batch_trade.do?" 106 | params = { 107 | 'api_key': self.__apikey, 108 | 'symbol': symbol, 109 | 'contract_type': contractType, 110 | 'orders_data': orders_data, 111 | 'lever_rate': leverRate 112 | } 113 | params['sign'] = buildMySign(params, self.__secretkey) 114 | return httpPost(self.__url, FUTURE_BATCH_TRADE, params) 115 | 116 | # 期货取消订单 117 | def future_cancel(self, symbol, contractType, orderId): 118 | FUTURE_CANCEL = "/api/v1/future_cancel.do?" 119 | params = { 120 | 'api_key': self.__apikey, 121 | 'symbol': symbol, 122 | 'contract_type': contractType, 123 | 'order_id': orderId 124 | } 125 | params['sign'] = buildMySign(params, self.__secretkey) 126 | return httpPost(self.__url, FUTURE_CANCEL, params) 127 | 128 | # 期货获取订单信息 129 | def future_orderinfo(self, symbol, contractType, orderId, status, currentPage, pageLength): 130 | FUTURE_ORDERINFO = "/api/v1/future_order_info.do?" 131 | params = { 132 | 'api_key': self.__apikey, 133 | 'symbol': symbol, 134 | 'contract_type': contractType, 135 | 'order_id': orderId, 136 | 'status': status, 137 | 'current_page': currentPage, 138 | 'page_length': pageLength 139 | } 140 | params['sign'] = buildMySign(params, self.__secretkey) 141 | return httpPost(self.__url, FUTURE_ORDERINFO, params) 142 | 143 | # 期货逐仓账户信息 144 | def future_userinfo_4fix(self): 145 | FUTURE_INFO_4FIX = "/api/v1/future_userinfo_4fix.do?" 146 | params = {'api_key': self.__apikey} 147 | params['sign'] = buildMySign(params, self.__secretkey) 148 | return httpPost(self.__url, FUTURE_INFO_4FIX, params) 149 | 150 | # 期货逐仓持仓信息 151 | def future_position_4fix(self, symbol, contractType, type1): 152 | FUTURE_POSITION_4FIX = "/api/v1/future_position_4fix.do?" 153 | params = { 154 | 'api_key': self.__apikey, 155 | 'symbol': symbol, 156 | 'contract_type': contractType, 157 | 'type': type1 158 | } 159 | params['sign'] = buildMySign(params, self.__secretkey) 160 | return httpPost(self.__url, FUTURE_POSITION_4FIX, params) 161 | -------------------------------------------------------------------------------- /exchangeConnection/okcoin/okcoinSpotAPI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 用于访问OKCOIN 现货REST API 5 | from exchangeConnection.okcoin.httpMD5Util import buildMySign, httpGet, httpPost 6 | from utils.helper import * 7 | 8 | 9 | class OKCoinSpot: 10 | def __init__(self, url, apikey, secretkey): 11 | self.__url = url 12 | self.__apikey = apikey 13 | self.__secretkey = secretkey 14 | 15 | # 获取OKCOIN现货行情信息 16 | def ticker(self, symbol=''): 17 | TICKER_RESOURCE = "/api/v1/ticker.do" 18 | params = '' 19 | if symbol: 20 | params = 'symbol=%(symbol)s' % {'symbol': symbol} 21 | return httpGet(self.__url, TICKER_RESOURCE, params) 22 | 23 | # 获取OKCOIN现货市场深度信息 24 | def depth(self, symbol='', size=1): 25 | DEPTH_RESOURCE = "/api/v1/depth.do" 26 | params = '' 27 | if symbol: 28 | params = 'symbol=%(symbol)s&size=%(size)d' % {'symbol': symbol, 'size': size} 29 | return httpGet(self.__url, DEPTH_RESOURCE, params) 30 | 31 | # 获取OKCOIN现货历史交易信息 32 | def trades(self, symbol=''): 33 | TRADES_RESOURCE = "/api/v1/trades.do" 34 | params = '' 35 | if symbol: 36 | params = 'symbol=%(symbol)s' % {'symbol': symbol} 37 | return httpGet(self.__url, TRADES_RESOURCE, params) 38 | 39 | # 获取用户现货账户信息 40 | def userInfo(self): 41 | USERINFO_RESOURCE = "/api/v1/userinfo.do" 42 | params = {} 43 | params['api_key'] = self.__apikey 44 | params['sign'] = buildMySign(params, self.__secretkey) 45 | return httpPost(self.__url, USERINFO_RESOURCE, params) 46 | 47 | # 现货交易 48 | def trade(self, symbol, tradeType, price='', amount=''): 49 | TRADE_RESOURCE = "/api/v1/trade.do" 50 | params = { 51 | 'api_key': self.__apikey, 52 | 'symbol': symbol, 53 | 'type': tradeType 54 | } 55 | if price: 56 | params['price'] = price 57 | if amount: 58 | params['amount'] = amount 59 | 60 | params['sign'] = buildMySign(params, self.__secretkey) 61 | return httpPost(self.__url, TRADE_RESOURCE, params) 62 | 63 | # 现货批量下单 64 | def batchTrade(self, symbol, tradeType, orders_data): 65 | BATCH_TRADE_RESOURCE = "/api/v1/batch_trade.do" 66 | params = { 67 | 'api_key': self.__apikey, 68 | 'symbol': symbol, 69 | 'type': tradeType, 70 | 'orders_data': orders_data 71 | } 72 | params['sign'] = buildMySign(params, self.__secretkey) 73 | return httpPost(self.__url, BATCH_TRADE_RESOURCE, params) 74 | 75 | # 现货取消订单 76 | def cancelOrder(self, symbol, orderId): 77 | CANCEL_ORDER_RESOURCE = "/api/v1/cancel_order.do" 78 | params = { 79 | 'api_key': self.__apikey, 80 | 'symbol': symbol, 81 | 'order_id': orderId 82 | } 83 | params['sign'] = buildMySign(params, self.__secretkey) 84 | return httpPost(self.__url, CANCEL_ORDER_RESOURCE, params) 85 | 86 | # 现货订单信息查询 87 | def orderInfo(self, symbol, orderId): 88 | ORDER_INFO_RESOURCE = "/api/v1/order_info.do" 89 | params = { 90 | 'api_key': self.__apikey, 91 | 'symbol': symbol, 92 | 'order_id': orderId 93 | } 94 | params['sign'] = buildMySign(params, self.__secretkey) 95 | return httpPost(self.__url, ORDER_INFO_RESOURCE, params) 96 | 97 | # 现货批量订单信息查询 98 | def ordersInfo(self, symbol, orderId, tradeType): 99 | ORDERS_INFO_RESOURCE = "/api/v1/orders_info.do" 100 | params = { 101 | 'api_key': self.__apikey, 102 | 'symbol': symbol, 103 | 'order_id': orderId, 104 | 'type': tradeType 105 | } 106 | params['sign'] = buildMySign(params, self.__secretkey) 107 | return httpPost(self.__url, ORDERS_INFO_RESOURCE, params) 108 | 109 | # 现货获得历史订单信息 110 | def orderHistory(self, symbol, status, currentPage, pageLength): 111 | ORDER_HISTORY_RESOURCE = "/api/v1/order_history.do" 112 | params = { 113 | 'api_key': self.__apikey, 114 | 'symbol': symbol, 115 | 'status': status, 116 | 'current_page': currentPage, 117 | 'page_length': pageLength 118 | } 119 | params['sign'] = buildMySign(params, self.__secretkey) 120 | return httpPost(self.__url, ORDER_HISTORY_RESOURCE, params) 121 | 122 | # 获取最小订单数量 123 | # okcoin上比特币的交易数量是0.01的整数倍,莱特币交易数量是0.1的整数倍 124 | def getMinimumOrderQty(self, symbol): 125 | if symbol == COIN_TYPE_BTC_CNY: 126 | return 0.01 * 1 127 | else: 128 | return 0.1 * 1 129 | -------------------------------------------------------------------------------- /exchangeConnection/okcoin/testOkcoin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 客户端调用,用于查看API返回结果 5 | from exchangeConnection.okcoin.util import * 6 | 7 | # 现货API 8 | okcoinSpot = getOkcoinSpot() 9 | 10 | # 期货API 11 | okcoinFuture = getOkcoinFuture() 12 | 13 | print(u' 现货行情 ') 14 | print(okcoinSpot.ticker('btc_cny')) 15 | 16 | print(u' 现货深度 ') 17 | print(okcoinSpot.depth('btc_cny')) 18 | 19 | print(u' 用户现货账户信息 ') 20 | print(okcoinSpot.userInfo()) 21 | 22 | '''print (u' 现货下单 ') 23 | print (okcoinSpot.trade('btc_cny','buy_market','50'))''' 24 | 25 | print(u' 用户现货账户信息 ') 26 | print(okcoinSpot.userInfo()) 27 | 28 | print(u' 现货订单信息查询 ') 29 | print(type(okcoinSpot.orderInfo('btc_cny', '6122509921'))) 30 | print(okcoinSpot.orderInfo('btc_cny', '6122509921')) 31 | 32 | print(u' 现货下单 ') 33 | # print (okcoinSpot.trade('btc_cny','buy_market','50')) 34 | 35 | print(u' 用户现货账户信息 ') 36 | print(type(okcoinSpot.userInfo())) 37 | print(okcoinSpot.userInfo()) 38 | 39 | 40 | # print (u' 现货历史交易信息 ') 41 | # print (okcoinSpot.trades()) 42 | 43 | # print (u' 用户现货账户信息 ') 44 | # print (okcoinSpot.userInfo()) 45 | 46 | # print (u' 现货下单 ') 47 | # print (okcoinSpot.trade('ltc_usd','buy','0.1','0.2')) 48 | 49 | # print (u' 现货批量下单 ') 50 | # print (okcoinSpot.batchTrade('ltc_usd','buy','[{price:0.1,amount:0.2},{price:0.1,amount:0.2}]')) 51 | 52 | # print (u' 现货取消订单 ') 53 | # print (okcoinSpot.cancelOrder('ltc_usd','18243073')) 54 | 55 | # print (u' 现货订单信息查询 ') 56 | # print (okcoinSpot.orderInfo('ltc_usd','18243644')) 57 | 58 | # print (u' 现货批量订单信息查询 ') 59 | # print (okcoinSpot.ordersInfo('ltc_usd','18243800,18243801,18243644','0')) 60 | 61 | # print (u' 现货历史订单信息查询 ') 62 | # print (okcoinSpot.orderHistory('ltc_usd','0','1','2')) 63 | 64 | # print (u' 期货行情信息') 65 | # print (okcoinFuture.future_ticker('ltc_usd','this_week')) 66 | 67 | # print (u' 期货市场深度信息') 68 | # print (okcoinFuture.future_depth('btc_usd','this_week','6')) 69 | 70 | # print (u'期货交易记录信息') 71 | # print (okcoinFuture.future_trades('ltc_usd','this_week')) 72 | 73 | # print (u'期货指数信息') 74 | # print (okcoinFuture.future_index('ltc_usd')) 75 | 76 | # print (u'美元人民币汇率') 77 | # print (okcoinFuture.exchange_rate()) 78 | 79 | # print (u'获取预估交割价') 80 | # print (okcoinFuture.future_estimated_price('ltc_usd')) 81 | 82 | # print (u'获取全仓账户信息') 83 | # print (okcoinFuture.future_userinfo()) 84 | 85 | # print (u'获取全仓持仓信息') 86 | # print (okcoinFuture.future_position('ltc_usd','this_week')) 87 | 88 | # print (u'期货下单') 89 | # print (okcoinFuture.future_trade('ltc_usd','this_week','0.1','1','1','0','20')) 90 | 91 | # print (u'期货批量下单') 92 | # print (okcoinFuture.future_batchTrade('ltc_usd','this_week','[{price:0.1,amount:1,type:1,match_price:0},{price:0.1,amount:3,type:1,match_price:0}]','20')) 93 | 94 | # print (u'期货取消订单') 95 | # print (okcoinFuture.future_cancel('ltc_usd','this_week','47231499')) 96 | 97 | # print (u'期货获取订单信息') 98 | # print (okcoinFuture.future_orderInfo('ltc_usd','this_week','47231812','0','1','2')) 99 | 100 | # print (u'期货逐仓账户信息') 101 | # print (okcoinFuture.future_userinfo_4fix()) 102 | 103 | # print (u'期货逐仓持仓信息') 104 | # print (okcoinFuture.future_position_4fix('ltc_usd','this_week',1)) 105 | -------------------------------------------------------------------------------- /exchangeConnection/okcoin/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import accountConfig 5 | from exchangeConnection.okcoin.okcoinFutureAPI import OKCoinFuture 6 | from exchangeConnection.okcoin.okcoinSpotAPI import OKCoinSpot 7 | 8 | # 初始化ACCESS_KEY, SECRET_KEY, SERVICE_API 9 | ACCESS_KEY = accountConfig.OKCOIN["CNY_1"]["ACCESS_KEY"] 10 | SECRET_KEY = accountConfig.OKCOIN["CNY_1"]["SECRET_KEY"] 11 | SERVICE_API = accountConfig.OKCOIN["CNY_1"]["SERVICE_API"] 12 | 13 | 14 | # 现货API 15 | def getOkcoinSpot(): 16 | return OKCoinSpot(SERVICE_API, ACCESS_KEY, SECRET_KEY) 17 | 18 | 19 | # 期货API 20 | def getOkcoinFuture(): 21 | return OKCoinFuture(SERVICE_API, ACCESS_KEY, SECRET_KEY) 22 | -------------------------------------------------------------------------------- /liveStrategyEngine/BaseLiveStrategyEngine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | import datetime 11 | import logging 12 | 13 | import exchangeConnection.huobi.history as history 14 | from common.Account import Account 15 | from common.Data import Data 16 | from common.Errors import StartRunningTimeEmptyError 17 | from common.Log import WQLogger 18 | from common.Order import Order 19 | from common.Time import Time 20 | from common.UserData import UserData 21 | from exchangeConnection.huobi import huobiService 22 | from exchangeConnection.huobi.util import * 23 | from utils import helper 24 | 25 | 26 | class BaseLiveStrategyEngine(object): 27 | def __init__(self, strat, startRunningTime, orderWaitingTime, dataLogFixedTimeWindow, dailyExitTime=None): 28 | self.strat = strat 29 | self.user_data = UserData() 30 | self.strat.initialize(self) 31 | self.startRunningTime = startRunningTime 32 | self.timeInterval = history.frequency_to_seconds[self.frequency] # 每次循环结束之后睡眠的时间, 单位:秒 33 | self.orderWaitingTime = orderWaitingTime # 每次等待订单执行的最长时间 34 | self.dataLogFixedTimeWindow = dataLogFixedTimeWindow # 每隔固定的时间打印账单信息,单位:秒 35 | self.coinMarketType = helper.getCoinMarketTypeFromSecurity(self.security) 36 | self.dailyExitTime = dailyExitTime # 如果设置,则为每天程序的退出时间 37 | self.TimeFormatForFileName = "%Y%m%d%H%M%S%f" 38 | self.TimeFormatForLog = "%Y-%m-%d %H:%M:%S.%f" 39 | self.huobiService = huobiService 40 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 41 | self.huobi_min_quantity = self.huobiService.getMinimumOrderQty(coin_type) 42 | self.huobi_min_cash_amount = self.huobiService.getMinimumOrderCashAmount() 43 | self.last_data_log_time = None 44 | # order query retry maximum times 45 | self.huobi_order_query_retry_maximum_times = 5 46 | 47 | # setup timeLogger 48 | self.timeLogger = logging.getLogger('timeLog') 49 | self.timeLogger.setLevel(logging.DEBUG) 50 | timeLogHandler = logging.FileHandler(self.getTimeLogFileName()) 51 | timeLogHandler.setLevel(logging.DEBUG) 52 | consoleLogHandler = logging.StreamHandler() 53 | consoleLogHandler.setLevel(logging.DEBUG) 54 | # 定义handler的输出格式 55 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 56 | timeLogHandler.setFormatter(formatter) 57 | consoleLogHandler.setFormatter(formatter) 58 | # 给timeLogger添加handler 59 | self.timeLogger.addHandler(timeLogHandler) 60 | self.timeLogger.addHandler(consoleLogHandler) 61 | 62 | # setup dataLogger 63 | self.dataLogger = logging.getLogger('dataLog') 64 | self.dataLogger.setLevel(logging.DEBUG) 65 | dataLogHandler = logging.FileHandler(self.getDataLogFileName()) 66 | dataLogHandler.setLevel(logging.DEBUG) 67 | self.dataLogger.addHandler(dataLogHandler) 68 | 69 | # setup context.log 70 | self.log = WQLogger(self.timeLogger) 71 | 72 | # setup context.data 73 | data = Data() 74 | data.get_price = history.get_price 75 | data.get_current_price = history.get_current_price 76 | data.get_all_securities = history.get_all_securities 77 | self.data = data 78 | 79 | # setup context.order 80 | order = Order() 81 | order.buy = self.buy 82 | order.buy_limit = self.buy_limit 83 | order.sell = self.sell 84 | order.sell_limit = self.sell_limit 85 | self.order = order 86 | 87 | # setup context.account and context.account_initial 88 | self.account = Account() 89 | self.account_initial = Account() 90 | self.updateAccountInfo(initial_setup=True) 91 | 92 | # setup context.time 93 | self.time = Time(self.startRunningTime) 94 | 95 | # setup handle_data 96 | self.handle_data = self.strat.handle_data 97 | 98 | def updateAccountInfo(self, initial_setup=False): 99 | huobiAcct = self.getAccuntInfo() 100 | self.account.huobi_cny_btc = huobiAcct["huobi_cny_btc"] 101 | self.account.huobi_cny_ltc = huobiAcct["huobi_cny_ltc"] 102 | self.account.huobi_cny_cash = huobiAcct["huobi_cny_cash"] 103 | self.account.huobi_cny_btc_loan = huobiAcct["huobi_cny_btc_loan"] 104 | self.account.huobi_cny_ltc_loan = huobiAcct["huobi_cny_ltc_loan"] 105 | self.account.huobi_cny_cash_loan = huobiAcct["huobi_cny_cash_loan"] 106 | self.account.huobi_cny_btc_frozen = huobiAcct["huobi_cny_btc_frozen"] 107 | self.account.huobi_cny_ltc_frozen = huobiAcct["huobi_cny_ltc_frozen"] 108 | self.account.huobi_cny_cash_frozen = huobiAcct["huobi_cny_cash_frozen"] 109 | self.account.huobi_cny_total = huobiAcct["huobi_cny_total"] 110 | self.account.huobi_cny_net = huobiAcct["huobi_cny_net"] 111 | if initial_setup: 112 | self.account_initial.huobi_cny_btc = huobiAcct["huobi_cny_btc"] 113 | self.account_initial.huobi_cny_ltc = huobiAcct["huobi_cny_ltc"] 114 | self.account_initial.huobi_cny_cash = huobiAcct["huobi_cny_cash"] 115 | self.account_initial.huobi_cny_btc_loan = huobiAcct["huobi_cny_btc_loan"] 116 | self.account_initial.huobi_cny_ltc_loan = huobiAcct["huobi_cny_ltc_loan"] 117 | self.account_initial.huobi_cny_cash_loan = huobiAcct["huobi_cny_cash_loan"] 118 | self.account_initial.huobi_cny_btc_frozen = huobiAcct["huobi_cny_btc_frozen"] 119 | self.account_initial.huobi_cny_ltc_frozen = huobiAcct["huobi_cny_ltc_frozen"] 120 | self.account_initial.huobi_cny_cash_frozen = huobiAcct["huobi_cny_cash_frozen"] 121 | self.account_initial.huobi_cny_total = huobiAcct["huobi_cny_total"] 122 | self.account_initial.huobi_cny_net = huobiAcct["huobi_cny_net"] 123 | self.account_initial.initial_time = datetime.datetime.now() 124 | 125 | def getStartRunningTime(self): 126 | if self.startRunningTime is None: 127 | raise StartRunningTimeEmptyError("startRunningTime is not set yet!") 128 | return self.startRunningTime 129 | 130 | def getTimeLogFileName(self): 131 | return "log/%s_log_%s.txt" % ( 132 | self.strat.__name__, self.getStartRunningTime().strftime(self.TimeFormatForFileName)) 133 | 134 | def getDataLogFileName(self): 135 | return "data/%s_data_%s.data" % ( 136 | self.strat.__name__, self.getStartRunningTime().strftime(self.TimeFormatForFileName)) 137 | 138 | def timeLog(self, content, level=logging.INFO): 139 | if level == logging.DEBUG: 140 | self.timeLogger.debug(content) 141 | elif level == logging.INFO: 142 | self.timeLogger.info(content) 143 | elif level == logging.WARN: 144 | self.timeLogger.warn(content) 145 | elif level == logging.ERROR: 146 | self.timeLogger.error(content) 147 | elif level == logging.CRITICAL: 148 | self.timeLogger.critical(content) 149 | else: 150 | raise ValueError("unsupported logging level %d" % level) 151 | 152 | def getAccuntInfo(self): 153 | huobiAcct = self.huobiService.getAccountInfo(helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 154 | ACCOUNT_INFO) 155 | huobi_cny_cash = float(huobiAcct[u'available_cny_display']) 156 | huobi_cny_btc = float(huobiAcct[u'available_btc_display']) 157 | huobi_cny_ltc = float(huobiAcct[u'available_ltc_display']) 158 | huobi_cny_cash_loan = float(huobiAcct[u'loan_cny_display']) 159 | huobi_cny_btc_loan = float(huobiAcct[u'loan_btc_display']) 160 | huobi_cny_ltc_loan = float(huobiAcct[u'loan_ltc_display']) 161 | huobi_cny_cash_frozen = float(huobiAcct[u'frozen_cny_display']) 162 | huobi_cny_btc_frozen = float(huobiAcct[u'frozen_btc_display']) 163 | huobi_cny_ltc_frozen = float(huobiAcct[u'frozen_ltc_display']) 164 | huobi_cny_total = float(huobiAcct[u'total']) 165 | huobi_cny_net = float(huobiAcct[u'net_asset']) 166 | 167 | return { 168 | "huobi_cny_cash": huobi_cny_cash, 169 | "huobi_cny_btc": huobi_cny_btc, 170 | "huobi_cny_ltc": huobi_cny_ltc, 171 | "huobi_cny_cash_loan": huobi_cny_cash_loan, 172 | "huobi_cny_btc_loan": huobi_cny_btc_loan, 173 | "huobi_cny_ltc_loan": huobi_cny_ltc_loan, 174 | "huobi_cny_cash_frozen": huobi_cny_cash_frozen, 175 | "huobi_cny_btc_frozen": huobi_cny_btc_frozen, 176 | "huobi_cny_ltc_frozen": huobi_cny_ltc_frozen, 177 | "huobi_cny_total": huobi_cny_total, 178 | "huobi_cny_net": huobi_cny_net 179 | } 180 | 181 | def dataLog(self, content=None): 182 | if content is None: 183 | accountInfo = self.getAccuntInfo() 184 | t = datetime.datetime.now() 185 | content = "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" % \ 186 | (t.strftime(self.TimeFormatForLog), 187 | accountInfo["huobi_cny_cash"], 188 | accountInfo["huobi_cny_btc"], 189 | accountInfo["huobi_cny_ltc"], 190 | accountInfo["huobi_cny_cash_loan"], 191 | accountInfo["huobi_cny_btc_loan"], 192 | accountInfo["huobi_cny_ltc_loan"], 193 | accountInfo["huobi_cny_cash_frozen"], 194 | accountInfo["huobi_cny_btc_frozen"], 195 | accountInfo["huobi_cny_ltc_frozen"], 196 | accountInfo["huobi_cny_total"], 197 | accountInfo["huobi_cny_net"]) 198 | self.last_data_log_time = t 199 | self.dataLogger.info("%s" % str(content)) 200 | 201 | def sell_limit(self, security, price, quantity): 202 | # TODO: implement the sell limit function 203 | self.sell(security, quantity) 204 | 205 | def sell(self, security, quantity): # quantity is a string value 206 | self.timeLog("开始下达火币市价卖单...") 207 | self.timeLog("只保留下单数量的小数点后4位...") 208 | self.timeLog("原始下单数量:%s" % quantity) 209 | tmp = float(quantity) 210 | tmp = helper.downRound(tmp, 4) 211 | quantity = str(tmp) 212 | self.timeLog("做完小数点处理后的下单数量:%s" % quantity) 213 | if float(quantity) < self.huobi_min_quantity: 214 | self.timeLog( 215 | "数量:%s 小于交易所最小交易数量(火币最小数量:%f),因此无法下单,此处忽略该信号" % (quantity, self.huobi_min_quantity), 216 | level=logging.WARN) 217 | return None 218 | 219 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 220 | res = self.huobiService.sellMarket(coin_type, quantity, None, None, 221 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 222 | SELL_MARKET) 223 | if helper.componentExtract(res, u"result", "") != u'success': 224 | self.timeLog("下达火币市价卖单(数量:%s)失败, 结果是: %s!" % (quantity, helper.componentExtract(res, u"result", "")), 225 | level=logging.ERROR) 226 | return None 227 | order_id = res[u"id"] 228 | # 查询订单执行情况 229 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 230 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 231 | "market"], ORDER_INFO) 232 | self.timeLog("下达如下火币市价卖单,数量:%s" % quantity) 233 | self.timeLog(str(order_info)) 234 | 235 | retry_time = 0 236 | while retry_time < self.huobi_order_query_retry_maximum_times and order_info["status"] != 2: 237 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 238 | time.sleep(self.orderWaitingTime) 239 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 240 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 241 | "market"], ORDER_INFO) 242 | self.timeLog(str(order_info)) 243 | retry_time += 1 244 | 245 | executed_qty = float(order_info["processed_amount"]) 246 | self.timeLog( 247 | "火币市价卖单已被执行,执行数量:%f,收到的现金:%.2f" % (executed_qty, executed_qty * float(order_info["processed_price"]))) 248 | 249 | # self.dataLog() 250 | return executed_qty 251 | 252 | def buy_limit(self, security, price, quantity): 253 | # TODO: implement the buy limit function 254 | self.buy(security, str(helper.downRound(float(price) * float(quantity), 2))) 255 | 256 | def buy(self, security, cash_amount): # cash_amount is a string value 257 | self.timeLog("开始下达火币市价买单...") 258 | self.timeLog("只保留下单数量的小数点后2位...") 259 | self.timeLog("原始下单金额:%s" % cash_amount) 260 | tmp = float(cash_amount) 261 | tmp = helper.downRound(tmp, 2) 262 | cash_amount = str(tmp) 263 | self.timeLog("做完小数点处理后的下单金额:%s" % cash_amount) 264 | 265 | if float(cash_amount) < self.huobi_min_cash_amount: 266 | self.timeLog("金额:%s 小于交易所最小交易金额(火币最小金额:%.2f元),因此无法下单,此处忽略该信号" % (cash_amount, self.huobi_min_cash_amount), 267 | level=logging.WARN) 268 | return None 269 | 270 | coin_type = helper.coinTypeStructure[self.coinMarketType]["huobi"]["coin_type"] 271 | res = self.huobiService.buyMarket(coin_type, cash_amount, None, None, 272 | helper.coinTypeStructure[self.coinMarketType]["huobi"]["market"], 273 | BUY_MARKET) 274 | if helper.componentExtract(res, u"result", "") != u'success': 275 | self.timeLog("下达火币市价买单(金额:%s)失败, 结果是:%s!" % (cash_amount, helper.componentExtract(res, u"result", "")), 276 | level=logging.ERROR) 277 | return None 278 | order_id = res[u"id"] 279 | # 查询订单执行情况 280 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 281 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 282 | "market"], ORDER_INFO) 283 | self.timeLog("下达如下火币市价买单,金额:%s" % cash_amount) 284 | self.timeLog(str(order_info)) 285 | 286 | retry_time = 0 287 | while retry_time < self.huobi_order_query_retry_maximum_times and order_info["status"] != 2: 288 | self.timeLog("等待%f秒直至订单完成" % self.orderWaitingTime) 289 | time.sleep(self.orderWaitingTime) 290 | order_info = self.huobiService.getOrderInfo(coin_type, order_id, 291 | helper.coinTypeStructure[self.coinMarketType]["huobi"][ 292 | "market"], ORDER_INFO) 293 | self.timeLog(str(order_info)) 294 | retry_time += 1 295 | 296 | if float(order_info["processed_price"]) > 0: 297 | executed_qty = float(order_info["processed_amount"]) / float(order_info["processed_price"]) 298 | self.timeLog("火币市价买单已被执行,执行数量:%f,花费的现金:%.2f" % (executed_qty, float(order_info["processed_amount"]))) 299 | # self.dataLog() 300 | return executed_qty 301 | else: 302 | self.timeLog("火币市价买单未被执行", level=logging.WARN) 303 | return 0 304 | 305 | def go(self): 306 | self.timeLog("日志启动于 %s" % self.getStartRunningTime().strftime(self.TimeFormatForLog)) 307 | self.dataLog( 308 | content="time|huobi_cny_cash|huobi_cny_btc|huobi_cny_ltc|huobi_cny_cash_loan|huobi_cny_btc_loan|huobi_cny_ltc_loan|huobi_cny_cash_frozen|huobi_cny_btc_frozen|huobi_cny_ltc_frozen|huobi_cny_total|huobi_cny_net") 309 | self.dataLog() 310 | 311 | while (True): 312 | # check whether current time is after the dailyExitTime, if yes, exit 313 | if self.dailyExitTime is not None and datetime.datetime.now() > datetime.datetime.strptime( 314 | datetime.date.today().strftime("%Y-%m-%d") + " " + self.dailyExitTime, 315 | "%Y-%m-%d %H:%M:%S"): 316 | self.timeLog("抵达每日终结时间:%s, 现在退出." % self.dailyExitTime) 317 | break 318 | 319 | self.timeLog("等待 %d 秒进入下一个循环..." % self.timeInterval) 320 | time.sleep(self.timeInterval) 321 | # TODO: to remove this line in production 322 | # time.sleep(5) 323 | 324 | # calculate the net asset at a fixed time window 325 | time_diff = datetime.datetime.now() - self.last_data_log_time 326 | if time_diff.seconds > self.dataLogFixedTimeWindow: 327 | self.dataLog() 328 | 329 | self.updateAccountInfo() 330 | self.handle_data(self) 331 | -------------------------------------------------------------------------------- /liveStrategyEngine/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | -------------------------------------------------------------------------------- /log/dummy_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/log/dummy_file -------------------------------------------------------------------------------- /main_banZhuan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | ############################################################### 5 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 6 | # 群主邮箱:lanjason@foxmail.com,群主微信/QQ:64008672 7 | # 沉迷量化,无法自拔 8 | ############################################################### 9 | 10 | from banZhuan.banZhuanStrategy import * 11 | from banZhuan.fixedSpreadArbStrategy import * 12 | from banZhuan.statArbStrategy import * 13 | from utils.helper import * 14 | 15 | if __name__ == "__main__": 16 | # 传统搬砖 - BTC 17 | ''' 18 | strat =BanZhuanStrategy(datetime.datetime.now(), 0.8, 1, 0.1, 60, helper.COIN_TYPE_BTC_CNY) 19 | ''' 20 | 21 | # 传统搬砖 - LTC, 加每日退出时间 22 | ''' 23 | strat =BanZhuanStrategy(datetime.datetime.now(), 0.8, 1, 0.1, 60, helper.COIN_TYPE_BTC_CNY, 24 | dailyExitTime="23:30:00") 25 | ''' 26 | 27 | # 统计套利搬砖 - BTC, 加每日退出时间, 加自动再平衡,加退出时自动再平衡 28 | ''' 29 | strat = StatArbSignalGenerator(datetime.datetime.now(), 0.8, 1, 0.1, 60, helper.COIN_TYPE_BTC_CNY, 30 | maximum_qty_multiplier=3, 31 | auto_rebalance_on=True, 32 | auto_rebalance_on_exit=True, 33 | dailyExitTime="23:30:00") 34 | ''' 35 | 36 | # 统计套利搬砖 - LTC 37 | ''' 38 | strat = StatArbSignalGenerator(datetime.datetime.now(), 0.8, 1, 0.1, 60, helper.COIN_TYPE_BTC_CNY, 39 | maximum_qty_multiplier=3, 40 | auto_rebalance_on=False, 41 | auto_rebalance_on_exit=False) 42 | ''' 43 | 44 | # 固定价差套利搬砖 - BTC 45 | strat = FixedSpreadSignalGenerator(datetime.datetime.now(), 0.8, 1, 0.1, 60, helper.COIN_TYPE_BTC_CNY, 46 | 0.001, 47 | 0.0005, 48 | maximum_qty_multiplier=3, 49 | auto_rebalance_on=False, 50 | auto_rebalance_on_exit=False) 51 | 52 | # 固定价差套利搬砖 - LTC, 加每日退出时间,加自动再平衡,加退出时自动再平衡 53 | ''' 54 | strat = FixedSpreadSignalGenerator(datetime.datetime.now(), 0.8, 1, 0.1, 60, helper.COIN_TYPE_LTC_CNY, 55 | 0.003, 56 | 0.001, 57 | maximum_qty_multiplier=3, 58 | auto_rebalance_on=True, 59 | auto_rebalance_on_exit=True) 60 | ''' 61 | start_strat(strat) -------------------------------------------------------------------------------- /main_userStrategy.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | import userStrategy.DualThrust as DualThrust 6 | from liveStrategyEngine.BaseLiveStrategyEngine import BaseLiveStrategyEngine 7 | from utils.helper import * 8 | 9 | if __name__ == "__main__": 10 | 11 | # 请不要跑userStrategy里面的策略,因为历史数据现在是Mock出来的 12 | 13 | 14 | ''' 15 | #simpleMA 16 | strat = BaseLiveStrategyEngine( SimpleMA,datetime.datetime.now(), 0.1, 30, dailyExitTime="23:30:00") 17 | ''' 18 | 19 | ''' 20 | #SeaTurtle 21 | strat = BaseLiveStrategyEngine( SeaTurtle,datetime.datetime.now(), 0.1, 30, dailyExitTime="23:30:00") 22 | ''' 23 | 24 | ''' 25 | #网格策略 26 | strat = BaseLiveStrategyEngine( Grid,datetime.datetime.now(), 0.1, 30, dailyExitTime="23:30:00") 27 | ''' 28 | 29 | ''' 30 | #价值定投策略 31 | strat = BaseLiveStrategyEngine( FixedPosValueGrowth,datetime.datetime.now(), 0.1, 30, dailyExitTime="23:30:00") 32 | ''' 33 | 34 | # DualThrust追涨杀跌策略 35 | 36 | # 请不要跑userStrategy里面的策略,因为历史数据现在是Mock出来的 37 | #strat = BaseLiveStrategyEngine(DualThrust, datetime.datetime.now(), 0.1, 30, dailyExitTime="23:30:00") 38 | #start_strat(strat) 39 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | ############################################################### 2 | # 获取更多免费策略,请加入WeQuant比特币量化策略交流QQ群:519538535 3 | # WeQuant微宽网 - https://wequant.io 4 | # 比特币量化交易/优质策略源码/精准回测/免费实盘,尽在微宽网 5 | ############################################################### 6 | 7 | 8 | 一、如何跑搬砖策略? 9 | 10 | 第一步:下载Python3,建议安装Anaconda 4.2.0(https://www.continuum.io/downloads),里面包含了Python 3.5 以及各种科学计算库 11 | 第二步:去huobi.com注册用户,申请API key,将申请好的API key设置到accountConfig.py中的HUOBI部分去 12 | 第三步:去www.okcoin.cn注册用户,申请API key,将申请好的API key设置到accountConfig.py中的OKCOIN部分去 13 | 第四步:搬砖策略入口脚本是main_banZhuan.py,如果想跑莱特币搬砖,直接注释掉比特币那两句,去掉莱特币那一段的注释即可 14 | 15 | 16 | 二、如何跑其他在userStrategy目录下的实盘非搬砖策略? 17 | 执行main_userStrategy.py,如果要跑其他策略,参考simpleMA对main_userStrategy.py进行修改。之后持续分享的策略都会放在userStrategy目录下. 18 | 19 | 三、怎么执行main_banZhuan.py? 20 | 如果是Mac或者Linux系统,直接在本项目的根目录下: 21 | chmod a+x run_banZhuan.sh 22 | ./run_banZhuan.sh 23 | 24 | 如果是Windows系统,直接在本项目的根目录下:参考run_banZhuan.bat修改成您的系统对应的各项参数,然后直接运行run_banZhuan.bat 25 | 26 | 四、怎么执行main_userStrategy.py? 27 | 请不要跑main_userStrategy里面的策略,因为历史数据现在是Mock出来的 28 | 29 | 今后拿到实盘数据的话,可以尝试跑一下。怎么跑? 30 | 如果是Mac或者Linux系统,直接在本项目的根目录下: 31 | chmod a+x run_userStrategy.sh 32 | ./run_userStrategy.sh 33 | 如果是Windows系统,直接在本项目的根目录下:参考run_userStrategy.bat修改成您的系统对应的各项参数,然后直接运行run_userStrategy.bat 34 | 35 | 36 | 五、FAQ: 37 | 1. 我想让我的策略7*24小时不间断运行,怎么做? 38 | 39 | 不用设置dailyExitTime即可 40 | BanzhuanStrategy(....., dailyExitTime="23:30:00") ==> BanzhuanStrategy(.....) 41 | 42 | 2. 我想让我的策略每天23:00终止,怎么做? 43 | 设置dailyExitTime为"23:00:00" 44 | BanzhuanStrategy(....., dailyExitTime="23:00:00") 45 | 46 | 3. 去哪里查看日志? 47 | log目录下有每次运行的日志,日志名里面有当次运行的起始时刻(精确到毫秒) 48 | 49 | 4. 去哪里看持仓记录? 50 | data目录下有每次运行的持仓记录,持仓记录名里面有当次运行的起始时刻(精确到毫秒) 51 | 52 | 5. 怎么跑BitVC期货策略 53 | 去http://www.bitvc.com/注册用户,申请API key,将申请好的API key设置到accountConfig.py中的BITVC部分去 54 | 55 | 56 | -------------------------------------------------------------------------------- /run_banZhuan.bat: -------------------------------------------------------------------------------- 1 | rem 1、先将下面PYTHONPATH的路径改为你本机上wequantstrategy_sample路径,如 PYTHONPATH=E:\wequantstrategy_sample 2 | rem 2、将Python3.5的路径改为你本机路径,就是python.exe 所在路径如C:\Python\Python35 3 | 4 | @set PYTHONPATH=E:\wequantstrategy_sample 5 | @set PythonDirectory=C:\Python\Python35 6 | 7 | cd %PYTHONPATH% 8 | %PythonDirectory%\python.exe main_banZhuan.py 9 | 10 | 11 | pause -------------------------------------------------------------------------------- /run_banZhuan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # run_banZhuan.sh 3 | 4 | now=`pwd` 5 | 6 | #替换字符串 7 | pythonpath=$now 8 | 9 | #执行 10 | PYTHONPATH=$pythonpath python main_banZhuan.py -------------------------------------------------------------------------------- /run_userStrategy.bat: -------------------------------------------------------------------------------- 1 | rem 1、先将下面PYTHONPATH的路径改为你本机上wequantstrategy_sample路径,如 PYTHONPATH=E:\wequantstrategy_sample 2 | rem 2、将Python3.5的路径改为你本机路径,就是python.exe 所在路径如C:\Python\Python35 3 | 4 | @set PYTHONPATH=E:\wequantstrategy_sample 5 | @set PythonDirectory=C:\Python\Python35 6 | 7 | cd %PYTHONPATH% 8 | %PythonDirectory%\python.exe main_userStrategy.py 9 | 10 | 11 | pause -------------------------------------------------------------------------------- /run_userStrategy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # run_userStrategy.sh 3 | 4 | now=`pwd` 5 | 6 | #替换字符串 7 | pythonpath=$now 8 | 9 | #执行 10 | PYTHONPATH=$pythonpath python main_userStrategy.py -------------------------------------------------------------------------------- /userStrategy/DualThrust.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 策略代码总共分为三大部分,1)PARAMS变量 2)intialize函数 3)handle_data函数 5 | # 请根据指示阅读。或者直接点击运行回测按钮,进行测试,查看策略效果。 6 | 7 | # 策略名称:Dual Thrust策略 8 | # 关键词:追涨杀跌、价格通道、止损。 9 | # 方法: 10 | # 1)根据一段时间内的最高价,最低价和收盘价,计算出一个价格上下限; 11 | # 2)当前价格突破上限时,全仓买入;当价格突破下线时,全仓卖出; 12 | # 3)加入了止盈止损机制。 13 | 14 | import numpy as np 15 | 16 | # 阅读1,首次阅读可跳过: 17 | # PARAMS用于设定程序参数,回测的起始时间、结束时间、滑点误差、初始资金和持仓。 18 | # 可以仿照格式修改,基本都能运行。如果想了解详情请参考新手学堂的API文档。 19 | PARAMS = { 20 | "start_time": "2015-01-01 00:00:00", # 回测起始时间 21 | "end_time": "2016-10-10 00:00:00", # 回测结束时间 22 | "slippage": 0.00001, # 设置滑点 23 | "account_initial": {"huobi_cny_cash": 100000, 24 | "huobi_cny_btc": 0}, # 设置账户初始状态 25 | } 26 | 27 | 28 | # 阅读2,遇到不明白的变量可以跳过,需要的时候回来查阅: 29 | # initialize函数是两大核心函数之一(另一个是handle_data),用于初始化策略变量。 30 | # 策略变量包含:必填变量,以及非必填(用户自己方便使用)的变量 31 | def initialize(context): 32 | # 以日为单位进行回测 33 | context.frequency = "1d" 34 | # 设定以比特币为基准 35 | context.benchmark = "huobi_cny_btc" 36 | # 设定操作的标的为比特币 37 | context.security = "huobi_cny_btc" 38 | 39 | # 设置策略参数 40 | # 计算HH,HC,LC,LL所需的历史bar数目,用户自定义的变量,可以被handle_data使用;如果只需要看之前1根bar,则定义window_size=1 41 | context.user_data.window_size = 5 42 | # 用户自定义的变量,可以被handle_data使用,触发多头的range 43 | context.user_data.K1 = 0.2 44 | # 用户自定义的变量,可以被handle_data使用,触发空头的range.当K1K2时,空头相对容易被触发 45 | context.user_data.K2 = 0.5 46 | # 止损线,用户自定义的变量,可以被handle_data使用 47 | context.user_data.portfolio_stop_loss = 0.75 48 | # 用户自定义变量,记录下是否已经触发止损 49 | context.user_data.stop_loss_triggered = False 50 | # 止盈线,用户自定义的变量,可以被handle_data使用 51 | context.user_data.portfolio_stop_win = 5.0 52 | # 用户自定义变量,记录下是否已经触发止盈 53 | context.user_data.stop_win_triggered = False 54 | 55 | 56 | # 阅读3,策略核心逻辑: 57 | # handle_data函数定义了策略的执行逻辑,按照frequency生成的bar依次读取并执行策略逻辑,直至程序结束。 58 | # handle_data和bar的详细说明,请参考新手学堂的解释文档。 59 | def handle_data(context): 60 | # 若已触发止盈/止损线,不会有任何操作 61 | if context.user_data.stop_loss_triggered: 62 | context.log.warn("已触发止损线, 此bar不会有任何指令 ... ") 63 | return 64 | if context.user_data.stop_win_triggered: 65 | context.log.info("已触发止盈线, 此bar不会有任何指令 ... ") 66 | return 67 | 68 | # 检查是否到达止损线或者止盈线 69 | if context.account.huobi_cny_net < context.user_data.portfolio_stop_loss * context.account_initial.huobi_cny_net or context.account.huobi_cny_net > context.user_data.portfolio_stop_win * context.account_initial.huobi_cny_net: 70 | should_stopped = True 71 | else: 72 | should_stopped = False 73 | 74 | # 如果有止盈/止损信号,则强制平仓,并结束所有操作 75 | if should_stopped: 76 | # 低于止损线,需要止损 77 | if context.account.huobi_cny_net < context.user_data.portfolio_stop_loss * context.account_initial.huobi_cny_net: 78 | context.log.warn( 79 | "当前净资产:%.2f 位于止损线下方 (%f), 初始资产:%.2f, 触发止损动作" % 80 | (context.account.huobi_cny_net, context.user_data.portfolio_stop_loss, 81 | context.account_initial.huobi_cny_net)) 82 | context.user_data.stop_loss_triggered = True 83 | # 高于止盈线,需要止盈 84 | else: 85 | context.log.warn( 86 | "当前净资产:%.2f 位于止盈线上方 (%f), 初始资产:%.2f, 触发止盈动作" % 87 | (context.account.huobi_cny_net, context.user_data.portfolio_stop_win, 88 | context.account_initial.huobi_cny_net)) 89 | context.user_data.stop_win_triggered = True 90 | 91 | if context.user_data.stop_loss_triggered: 92 | context.log.info("设置 stop_loss_triggered(已触发止损信号)为真") 93 | else: 94 | context.log.info("设置 stop_win_triggered (已触发止损信号)为真") 95 | 96 | # 需要止盈/止损,卖出全部持仓 97 | if getattr(context.account, context.security) > 0: 98 | # 卖出时,全仓清空 99 | context.log.info("正在卖出 %s" % context.security) 100 | context.order.sell(context.security, quantity=str(getattr(context.account, context.security))) 101 | return 102 | 103 | # 获取历史数据, 取后window_size+1根bar 104 | hist = context.data.get_price(context.security, count=context.user_data.window_size + 1, 105 | frequency=context.frequency) 106 | # 判断读取数量是否正确 107 | if len(hist.index) < (context.user_data.window_size + 1): 108 | context.log.warn("bar的数量不足, 等待下一根bar...") 109 | return 110 | 111 | # 取得最近1 根 bar的close价格 112 | latest_close_price = context.data.get_current_price(context.security) 113 | 114 | # 开始计算N日最高价的最高价HH,N日收盘价的最高价HC,N日收盘价的最低价LC,N日最低价的最低价LL 115 | hh = np.max(hist['high'].tail(context.user_data.window_size)) 116 | hc = np.max(hist['close'].tail(context.user_data.window_size)) 117 | lc = np.min(hist['close'].tail(context.user_data.window_size)) 118 | ll = np.min(hist['low'].tail(context.user_data.window_size)) 119 | price_range = max(hh - lc, hc - ll) 120 | 121 | # 取得倒数第二根bar的close, 并计算上下界限 122 | up_bound = hist['close'].iloc[-2] + context.user_data.K1 * price_range 123 | low_bound = hist['close'].iloc[-2] - context.user_data.K2 * price_range 124 | 125 | context.log.info('当前 价格:%s, 上轨:%s, 下轨: %s' % (latest_close_price, up_bound, low_bound)) 126 | 127 | # 产生买入卖出信号,并执行操作 128 | if latest_close_price > up_bound: 129 | if context.account.huobi_cny_cash > 0: 130 | # 买入信号,且持有现金,则市价单全仓买入 131 | context.log.info("价格突破上轨,产生买入信号") 132 | context.log.info("正在买入 %s" % context.security) 133 | context.log.info("下单金额为 %s 元" % context.account.huobi_cny_cash) 134 | context.order.buy(context.security, cash_amount=str(context.account.huobi_cny_cash)) 135 | elif latest_close_price < low_bound: 136 | if getattr(context.account, context.security) > 0: 137 | # 卖出信号,且持有仓位,则市价单全仓卖出 138 | context.log.info("价格突破下轨,产生卖出信号") 139 | context.log.info("正在卖出 %s" % context.security) 140 | context.log.info("卖出数量为 %s" % getattr(context.account, context.security)) 141 | context.order.sell(context.security, quantity=str(getattr(context.account, context.security))) 142 | -------------------------------------------------------------------------------- /userStrategy/FixedPosValueGrowth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 策略代码总共分为三大部分,1)PARAMS变量 2)intialize函数 3)handle_data函数 5 | # 请根据指示阅读。或者直接点击运行回测按钮,进行测试,查看策略效果。 6 | 7 | # 策略名称:价值平均定投策略 8 | # 关键词:长期投资、高抛低吸、分批建仓。 9 | # 方法: 10 | # 1)确定每个周期的目标仓位; 11 | # 2)每一期将仓位调整至目标仓位; 12 | 13 | 14 | # 阅读1,首次阅读可跳过: 15 | # PARAMS用于设定程序参数,回测的起始时间、结束时间、滑点误差、初始资金和持仓。 16 | # 可以仿照格式修改,基本都能运行。如果想了解详情请参考新手学堂的API文档。 17 | PARAMS = { 18 | "start_time": "2015-01-01 00:00:00", 19 | "end_time": "2016-09-01 00:00:00", 20 | "slippage": 0.00001, 21 | "account_initial": {"huobi_cny_cash": 60000, 22 | "huobi_cny_btc": 0}, 23 | } 24 | 25 | 26 | # 阅读2,遇到不明白的变量可以跳过,需要的时候回来查阅: 27 | # initialize函数是两大核心函数之一(另一个是handle_data),用于初始化策略变量。 28 | # 策略变量包含:必填变量,以及非必填(用户自己方便使用)的变量 29 | def initialize(context): 30 | # 以日为单位进行回测 31 | context.frequency = "1d" 32 | # 设定以比特币为基准 33 | context.benchmark = "huobi_cny_btc" 34 | # 设定操作的标的为比特币 35 | context.security = "huobi_cny_btc" 36 | 37 | # 设置策略参数 38 | # 每个frequency的持仓总值的增长金额 39 | context.user_data.pos_value_growth_per_period = 100 40 | # 记录下当前处于第几个投资周期 41 | context.user_data.invest_period_count = 0 42 | # 设置策略期望初始仓位 43 | context.user_data.initial_pos_value = 0 44 | 45 | 46 | # 阅读3,策略核心逻辑: 47 | # handle_data函数定义了策略的执行逻辑,按照frequency生成的bar依次读取并执行策略逻辑,直至程序结束。 48 | # handle_data和bar的详细说明,请参考新手学堂的解释文档。 49 | def handle_data(context): 50 | # 取得最新价格 51 | latest_close_price = context.data.get_current_price(context.security) 52 | # 计算当前实时仓位 53 | current_pos_value = getattr(context.account, context.security) * latest_close_price 54 | 55 | if context.user_data.initial_pos_value is None: 56 | context.user_data.initial_pos_value = current_pos_value 57 | 58 | # 计算当前期望仓位 59 | expected_pos_value = context.user_data.initial_pos_value + context.user_data.pos_value_growth_per_period * ( 60 | context.user_data.invest_period_count + 1) 61 | # 当前账户持有的人民币现金 62 | current_cash_pos = context.account.huobi_cny_cash 63 | # 当前账户持有的数字货币数量 64 | current_sec_pos = getattr(context.account, context.security) 65 | # 计算本期需要投入的资金(若为负,则是撤回的资金) 66 | cash_to_spent = cash_to_spent_fn(context, expected_pos_value, current_pos_value, current_cash_pos, current_sec_pos, 67 | latest_close_price) 68 | context.log.info("本期需要投入的现金:%f元" % cash_to_spent) 69 | 70 | # 更新投资周期至下一期 71 | context.user_data.invest_period_count += 1 72 | 73 | if cash_to_spent > 0: 74 | # 需要加仓,市价单买入 75 | context.log.info("正在买入%s" % context.security) 76 | context.log.info("下单金额为 %s 元" % round(cash_to_spent, 2)) 77 | context.order.buy(context.security, cash_amount=str(cash_to_spent)) 78 | else: 79 | # 需要减仓,计算需要卖出的数量,市价单卖出 80 | quantity = min(getattr(context.account, context.security), -1 * cash_to_spent / latest_close_price) 81 | context.log.info("正在卖出 %s" % context.security) 82 | context.log.info("卖出数量为 %s" % round(quantity)) 83 | context.order.sell(context.security, quantity=str(quantity)) 84 | 85 | 86 | # # 用户自定义的函数,可以被handle_data调用:计算每一个frequency需要买入/卖出的金额(正为买入,负为卖出) 87 | def cash_to_spent_fn(context, expected_pos_value, current_pos_value, current_cash_pos, current_sec_pos, 88 | latest_close_price): 89 | # 低于目标仓位,需要买入加仓 90 | if expected_pos_value > current_pos_value: 91 | result = expected_pos_value - current_pos_value 92 | if result < current_cash_pos: 93 | return result 94 | else: # 现金不足,投入全部现金加仓 95 | context.log.warn( 96 | "现金不足以满足目标仓位, 需要现金:%.2f, 现有现金:%.2f. 本次将用完全部现金" % (result, current_cash_pos)) 97 | return current_cash_pos 98 | else: # 当前仓位高于目标仓位,需要卖出减仓 99 | result = current_pos_value - expected_pos_value 100 | pos_qty_to_sell = result / latest_close_price 101 | if pos_qty_to_sell < current_sec_pos: 102 | return -1 * result 103 | else: # 仓位不足,卖出全部仓位 104 | context.log.warn( 105 | "现有仓位不足以满足目标仓位, 需要卖出仓位:%.2f, 现有仓位:%.2f. 本次将卖出所有仓位" % (pos_qty_to_sell, current_sec_pos)) 106 | return -1 * latest_close_price * current_sec_pos 107 | -------------------------------------------------------------------------------- /userStrategy/Grid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 策略代码总共分为三大部分,1)PARAMS变量 2)intialize函数 3)handle_data函数 5 | # 请根据指示阅读。或者直接点击运行回测按钮,进行测试,查看策略效果。 6 | 7 | # 策略名称:网格交易策略 8 | # 关键词:高抛低吸、逐步建仓。 9 | # 方法: 10 | # 1)设定一个基础价格,并围绕基础价格设置价格网格; 11 | # 2)在相应价格位置调整仓位至相应水平(高位减仓,地位加仓); 12 | # 3)在价格的波动中赚取收益。 13 | 14 | import numpy as np 15 | 16 | # 阅读1,首次阅读可跳过: 17 | # PARAMS用于设定程序参数,回测的起始时间、结束时间、滑点误差、初始资金和持仓。 18 | # 可以仿照格式修改,基本都能运行。如果想了解详情请参考新手学堂的API文档。 19 | PARAMS = { 20 | "start_time": "2015-01-01 00:00:00", # 回测起始时间 21 | "end_time": "2015-10-01 00:00:00", # 回测结束时间 22 | "slippage": 0.00001, # 设置滑点 23 | "account_initial": {"huobi_cny_cash": 100000, 24 | "huobi_cny_btc": 0}, # 设置账户初始状态 25 | } 26 | 27 | 28 | # 阅读2,遇到不明白的变量可以跳过,需要的时候回来查阅: 29 | # initialize函数是两大核心函数之一(另一个是handle_data),用于初始化策略变量。 30 | # 策略变量包含:必填变量,以及非必填(用户自己方便使用)的变量 31 | def initialize(context): 32 | # 以日为单位进行回测 33 | context.frequency = "1d" 34 | # 设定以比特币为基准 35 | context.benchmark = "huobi_cny_btc" 36 | # 设定操作的标的为比特币 37 | context.security = "huobi_cny_btc" 38 | 39 | # 设置策略参数 40 | # 底仓价格 41 | context.user_data.base_price = None 42 | # 计算移动均值所需的历史bar数目,用户自定义的变量,可以被handle_data使用 43 | context.user_data.sma_window_size = 20 44 | # 确定当前price可否作为base_price的依据就是当前price是否小于20日均线*price_to_sma_threshold 45 | context.user_data.price_to_sma_threshold = 0.85 46 | # 止损线,用户自定义的变量,可以被handle_data使用 47 | context.user_data.portfolio_stop_loss = 0.00 48 | # 用户自定义变量,记录下是否已经触发止损 49 | context.user_data.stop_loss_triggered = False 50 | # 止盈线,用户自定义的变量,可以被handle_data使用 51 | context.user_data.portfolio_stop_win = 5.0 52 | # 用户自定义变量,记录下是否已经触发止盈 53 | context.user_data.stop_win_triggered = False 54 | # 设置网格的4个档位的买入价格(相对于基础价的百分比) 55 | context.user_data.buy4, context.user_data.buy3, context.user_data.buy2, context.user_data.buy1 = 0.88, 0.91, 0.94, 0.97 56 | # 设置网格的4个档位的卖出价格(相对于基础价的百分比) 57 | context.user_data.sell4, context.user_data.sell3, context.user_data.sell2, context.user_data.sell1 = 1.2, 1.15, 1.1, 1.05 58 | 59 | 60 | # 阅读3,策略核心逻辑: 61 | # handle_data函数定义了策略的执行逻辑,按照frequency生成的bar依次读取并执行策略逻辑,直至程序结束。 62 | # handle_data和bar的详细说明,请参考新手学堂的解释文档。 63 | def handle_data(context): 64 | if context.user_data.stop_loss_triggered: 65 | context.log.warn("已触发止损线, 此bar不会有任何指令 ... ") 66 | return 67 | 68 | if context.user_data.stop_win_triggered: 69 | context.log.info("已触发止盈线, 此bar不会有任何指令 ... ") 70 | return 71 | 72 | # 检查是否到达止损线或者止盈线,如果是,强制平仓,并结束所有操作 73 | if context.account.huobi_cny_net < context.user_data.portfolio_stop_loss * context.account_initial.huobi_cny_net or context.account.huobi_cny_net > context.user_data.portfolio_stop_win * context.account_initial.huobi_cny_net: 74 | should_stopped = True 75 | else: 76 | should_stopped = False 77 | 78 | # 如果有止盈/止损信号,则强制平仓,并结束所有操作 79 | if should_stopped: 80 | # 低于止损线,需要止损 81 | if context.account.huobi_cny_net < context.user_data.portfolio_stop_loss * context.account_initial.huobi_cny_net: 82 | context.log.warn( 83 | "当前净资产:%.2f 位于止损线下方 (%f), 初始资产:%.2f, 触发止损动作" % 84 | (context.account.huobi_cny_net, context.user_data.portfolio_stop_loss, 85 | context.account_initial.huobi_cny_net)) 86 | context.user_data.stop_loss_triggered = True 87 | # 高于止盈线,需要止盈 88 | else: 89 | context.log.warn( 90 | "当前净资产:%.2f 位于止盈线上方 (%f), 初始资产:%.2f, 触发止盈动作" % 91 | (context.account.huobi_cny_net, context.user_data.portfolio_stop_win, 92 | context.account_initial.huobi_cny_net)) 93 | context.user_data.stop_win_triggered = True 94 | 95 | if context.user_data.stop_loss_triggered: 96 | context.log.info("设置 stop_loss_triggered(已触发止损信号)为真") 97 | else: 98 | context.log.info("设置 stop_win_triggered (已触发止损信号)为真") 99 | 100 | # 有止盈/止损,且当前有仓位,则强平所有仓位 101 | if getattr(context.account, context.security) > 0: 102 | context.log.info("正在卖出 %s" % context.security) 103 | context.order.sell(context.security, quantity=str(getattr(context.account, context.security))) 104 | return 105 | 106 | # 获取当前价格 107 | price = context.data.get_current_price(context.security) 108 | 109 | # 设置网格策略基础价格(base_price) 110 | if context.user_data.base_price is None: 111 | # 获取历史数据, 取后sma_window_size根bar 112 | hist = context.data.get_price(context.security, count=context.user_data.sma_window_size, frequency="1d") 113 | if len(hist.index) < context.user_data.sma_window_size: 114 | context.log.warn("bar的数量不足, 等待下一根bar...") 115 | return 116 | # 计算sma均线值 117 | sma = np.mean(hist['close'][-1 * context.user_data.sma_window_size:]) 118 | # 若当前价格满足条件,则设置当前价格为基础价 119 | if price < context.user_data.price_to_sma_threshold * sma and context.user_data.base_price is None: 120 | context.user_data.base_price = price 121 | 122 | # 还没有找到base_price,则继续找,不着急建仓 123 | if context.user_data.base_price is None: 124 | context.log.info('尚未找到合适的基准价格,进入下一根bar') 125 | return 126 | 127 | cash_to_spent = 0 128 | 129 | # 计算为达到目标仓位需要买入/卖出的金额 130 | # 价格低于buy4所对应的价格时,仓位调至100% 131 | if price / context.user_data.base_price < context.user_data.buy4: 132 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 1, context.account.huobi_cny_cash) 133 | # 价格大于等于buy4对应的价格,低于buy3所对应的价格时,仓位调至90% 134 | elif price / context.user_data.base_price < context.user_data.buy3: 135 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0.9, context.account.huobi_cny_cash) 136 | # 价格大于等于buy3对应的价格,低于buy2所对应的价格时,仓位调至70% 137 | elif price / context.user_data.base_price < context.user_data.buy2: 138 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0.7, context.account.huobi_cny_cash) 139 | # 价格大于等于buy2对应的价格,低于buy1所对应的价格时,仓位调至40% 140 | elif price / context.user_data.base_price < context.user_data.buy1: 141 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0.4, context.account.huobi_cny_cash) 142 | # 价格大于sell4对应的价格,仓位调至0% 143 | elif price / context.user_data.base_price > context.user_data.sell4: 144 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0, context.account.huobi_cny_cash) 145 | # 价格小于等于sell4对应的价格,大于sell3所对应的价格时,仓位调至10% 146 | elif price / context.user_data.base_price > context.user_data.sell3: 147 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0.1, context.account.huobi_cny_cash) 148 | # 价格小于等于sell3对应的价格,大于sell2所对应的价格时,仓位调至30% 149 | elif price / context.user_data.base_price > context.user_data.sell2: 150 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0.3, context.account.huobi_cny_cash) 151 | # 价格小于等于sell2对应的价格,大于sell1所对应的价格时,仓位调至60% 152 | elif price / context.user_data.base_price > context.user_data.sell1: 153 | cash_to_spent = cash_to_spent_fn(context.account.huobi_cny_net, 0.6, context.account.huobi_cny_cash) 154 | 155 | # 根据策略调整仓位 156 | if cash_to_spent > 0: 157 | # 市价单买入一定金额 158 | context.log.info("正在买入 %s" % context.security) 159 | context.log.info("下单金额为 %s 元" % context.account.huobi_cny_cash) 160 | context.order.buy(context.security, cash_amount=str(cash_to_spent)) 161 | elif cash_to_spent < 0: 162 | # 计算需要卖出的数量,并已市价单卖出 163 | quantity = min(getattr(context.account, context.security), -1 * cash_to_spent / price) 164 | context.log.info("正在卖出 %s" % context.security) 165 | context.order.sell(context.security, quantity=str(quantity)) 166 | 167 | 168 | # 计算为达到目标仓位所需要购买的金额 169 | def cash_to_spent_fn(net_asset, target_ratio, available_cny): 170 | return available_cny - net_asset * (1 - target_ratio) 171 | -------------------------------------------------------------------------------- /userStrategy/SeaTurtle.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 策略代码总共分为三大部分,1)PARAMS变量 2)intialize函数 3)handle_data函数 5 | # 请根据指示阅读。或者直接点击运行回测按钮,进行测试,查看策略效果。 6 | 7 | # 策略名称:海龟交易策略 8 | # 关键词:趋势跟随、资金管理、分批建仓、动态止损。 9 | # 方法: 10 | # 1)利用唐安奇通道来跟踪趋势产生买卖信号; 11 | # 2)利用ATR(真实波幅均值)分批加仓或者减仓; 12 | # 3)并且动态进行止盈和止损。 13 | 14 | 15 | import numpy as np 16 | 17 | # 阅读1,首次阅读可跳过: 18 | # PARAMS用于设定程序参数,回测的起始时间、结束时间、滑点误差、初始资金和持仓。 19 | # 可以仿照格式修改,基本都能运行。如果想了解详情请参考新手学堂的API文档。 20 | PARAMS = { 21 | "start_time": "2014-01-01 00:00:00", 22 | "end_time": "2016-10-01 00:00:00", 23 | "slippage": 0.00001, 24 | "account_initial": {"huobi_cny_cash": 100000, 25 | "huobi_cny_btc": 0}, 26 | } 27 | 28 | 29 | # 阅读2,遇到不明白的变量可以跳过,需要的时候回来查阅: 30 | # initialize函数是两大核心函数之一(另一个是handle_data),用于初始化策略变量。 31 | # 策略变量包含:必填变量,以及非必填(用户自己方便使用)的变量 32 | def initialize(context): 33 | # 设置回测频率 34 | context.frequency = "1d" 35 | # 设置回测基准 36 | context.benchmark = "huobi_cny_btc" 37 | # 设置回测标的 38 | context.security = "huobi_cny_btc" 39 | 40 | # 设置ATR值回看窗口 41 | context.user_data.T = 5 42 | 43 | # 自定义的初始化函数 44 | init_local_context(context) 45 | 46 | # 至此intialize函数定义完毕。 47 | 48 | 49 | # 阅读3,策略核心逻辑: 50 | # handle_data函数定义了策略的执行逻辑,按照frequency生成的bar依次读取并执行策略逻辑,直至程序结束。 51 | # handle_data和bar的详细说明,请参考新手学堂的解释文档。 52 | def handle_data(context): 53 | # 获取历史数据 54 | hist = context.data.get_price(context.security, count=context.user_data.T + 1, frequency=context.frequency) 55 | if len(hist.index) < (context.user_data.T + 1): 56 | context.log.warn("bar的数量不足, 等待下一根bar...") 57 | return 58 | 59 | # 获取当前行情数据 60 | price = context.data.get_current_price(context.security) 61 | 62 | # 1 计算ATR 63 | atr = calc_atr(hist.iloc[:len(hist) - 1]) 64 | 65 | # 2 判断加仓或止损 66 | if context.user_data.hold_flag is True and getattr(context.account, context.security) > 0: # 先判断是否持仓 67 | temp = add_or_stop(price, context.user_data.last_buy_price, atr, context) 68 | if temp == 1: # 判断加仓 69 | if context.user_data.add_time < context.user_data.limit_unit: # 判断加仓次数是否超过上限 70 | context.log.info("产生加仓信号") 71 | cash_amount = min(context.account.huobi_cny_cash, context.user_data.unit * price) # 不够1 unit时买入剩下全部 72 | context.user_data.last_buy_price = price 73 | context.user_data.add_time += 1 74 | context.log.info("正在买入 %s" % context.security) 75 | context.log.info("下单金额为 %s 元" % cash_amount) 76 | context.order.buy(context.security, cash_amount=str(cash_amount)) 77 | else: 78 | context.log.info("加仓次数已经达到上限,不会加仓") 79 | elif temp == -1: # 判断止损 80 | # 重新初始化参数!重新初始化参数!重新初始化参数!非常重要! 81 | init_local_context(context) 82 | # 卖出止损 83 | context.log.info("产生止损信号") 84 | context.log.info("正在卖出 %s" % context.security) 85 | context.log.info("卖出数量为 %s" % getattr(context.account, context.security)) 86 | context.order.sell(context.security, quantity=str(getattr(context.account, context.security))) 87 | else: 88 | # 3 判断入场离场 89 | out = in_or_out(context, hist.iloc[:len(hist) - 1], price, context.user_data.T) 90 | if out == 1: # 入场 91 | if context.user_data.hold_flag is False: 92 | value = min(context.account.huobi_cny_net * 0.01, context.account.huobi_cny_cash) 93 | context.user_data.unit = calc_unit(value, atr) 94 | context.user_data.add_time = 1 95 | context.user_data.hold_flag = True 96 | context.user_data.last_buy_price = price 97 | # 有买入信号,执行买入 98 | context.log.info("产生入场信号") 99 | context.log.info("正在买入 %s" % context.security) 100 | context.log.info("下单金额为 %s 元" % (context.user_data.unit * price)) 101 | context.order.buy(context.security, cash_amount=str(context.user_data.unit * price)) 102 | else: 103 | context.log.info("已经入场,不产生入场信号") 104 | elif out == -1: # 离场 105 | if context.user_data.hold_flag is True: 106 | if getattr(context.account, context.security) > 0: 107 | context.log.info("产生离场信号") 108 | # 重新初始化参数!重新初始化参数!重新初始化参数!非常重要! 109 | init_local_context(context) 110 | # 有卖出信号,且持有仓位,则市价单全仓卖出 111 | context.log.info("正在卖出 %s" % context.security) 112 | context.log.info("卖出数量为 %s" % getattr(context.account, context.security)) 113 | context.order.sell(context.security, quantity=str(getattr(context.account, context.security))) 114 | else: 115 | context.log.info("尚未入场或已经离场,不产生离场信号") 116 | 117 | 118 | # 用户自定义的函数,可以被handle_data调用:用于初始化一些用户数据 119 | def init_local_context(context): 120 | # 上一次买入价 121 | context.user_data.last_buy_price = 0 122 | # 是否持有头寸标志 123 | context.user_data.hold_flag = False 124 | # 限制最多买入的单元数 125 | context.user_data.limit_unit = 4 126 | # 现在买入1单元的security数目 127 | context.user_data.unit = 0 128 | # 买入次数 129 | context.user_data.add_time = 0 130 | 131 | 132 | # 用户自定义的函数,可以被handle_data调用: 唐奇安通道计算及判断入场离场 133 | # data是日线级别的历史数据,price是当前分钟线数据(用来获取当前行情),T代表需要多少根日线 134 | def in_or_out(context, data, price, T): 135 | up = np.max(data['high'].iloc[-T:]) 136 | # 这里是T/2唐奇安下沿,在向下突破T/2唐奇安下沿卖出而不是在向下突破T唐奇安下沿卖出,这是为了及时止损 137 | down = np.min(data['low'].iloc[-int(T / 2):]) 138 | context.log.info("当前价格为: %s, 唐奇安上轨为: %s, 唐奇安下轨为: %s" % (price, up, down)) 139 | # 当前价格升破唐奇安上沿,产生入场信号 140 | if price > up: 141 | context.log.info('价格突破唐奇安上轨') 142 | return 1 143 | # 当前价格跌破唐奇安下沿,产生出场信号 144 | elif price < down: 145 | context.log.info('价格跌破唐奇安下轨') 146 | return -1 147 | # 未产生有效信号 148 | else: 149 | return 0 150 | 151 | 152 | # 用户自定义的函数,可以被handle_data调用:ATR值计算 153 | def calc_atr(data): # data是日线级别的历史数据 154 | tr_list = [] 155 | for i in range(len(data)): 156 | tr = max(data['high'].iloc[i] - data['low'].iloc[i], data['high'].iloc[i] - data['close'].iloc[i - 1], 157 | data['close'].iloc[i - 1] - data['low'].iloc[i]) 158 | tr_list.append(tr) 159 | atr = np.array(tr_list).mean() 160 | return atr 161 | 162 | 163 | # 用户自定义的函数,可以被handle_data调用 164 | # 计算unit 165 | def calc_unit(per_value, atr): 166 | return per_value / atr 167 | 168 | 169 | # 用户自定义的函数,可以被handle_data调用 170 | # 判断是否加仓或止损:当价格相对上个买入价上涨 0.5ATR时,再买入一个unit; 当价格相对上个买入价下跌 2ATR时,清仓 171 | def add_or_stop(price, lastprice, atr, context): 172 | if price >= lastprice + 0.5 * atr: 173 | context.log.info('当前价格比上一个购买价格上涨超过0.5个ATR') 174 | return 1 175 | elif price <= lastprice - 2 * atr: 176 | context.log.info('当前价格比上一个购买价格下跌超过2个ATR') 177 | return -1 178 | else: 179 | return 0 180 | -------------------------------------------------------------------------------- /userStrategy/SimpleMA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # 策略代码总共分为三大部分,1)PARAMS变量 2)intialize函数 3)handle_data函数 5 | # 请根据指示阅读。或者直接点击运行回测按钮,进行测试,查看策略效果。 6 | 7 | # 策略名称:简单双均线策略 8 | # 关键词:价格突破、趋势跟踪。 9 | # 方法: 10 | # 1)计算一长一短两个时间窗口的价格均线 11 | # 2)利用均线的突破来决定买卖 12 | 13 | import numpy as np 14 | 15 | # 阅读1,首次阅读可跳过: 16 | # PARAMS用于设定程序参数,回测的起始时间、结束时间、滑点误差、初始资金和持仓。 17 | # 可以仿照格式修改,基本都能运行。如果想了解详情请参考新手学堂的API文档。 18 | PARAMS = { 19 | "start_time": "2016-01-01 00:00:00", # 回测起始时间 20 | "end_time": "2016-10-01 00:00:00", # 回测结束时间 21 | "slippage": 0.00001, # 设置滑点 22 | "account_initial": {"huobi_cny_cash": 100000, 23 | "huobi_cny_btc": 0}, # 设置账户初始状态 24 | } 25 | 26 | 27 | # 阅读2,遇到不明白的变量可以跳过,需要的时候回来查阅: 28 | # initialize函数是两大核心函数之一(另一个是handle_data),用于初始化策略变量。 29 | # 策略变量包含:必填变量,以及非必填(用户自己方便使用)的变量 30 | def initialize(context): 31 | # 以1分钟为单位进行回测 32 | context.frequency = "60m" 33 | # 设定以比特币为基准 34 | context.benchmark = "huobi_cny_btc" 35 | # 设定操作的标的为比特币 36 | context.security = "huobi_cny_btc" 37 | 38 | # 设置策略参数 39 | # 计算短线所需的历史bar数目,用户自定义的变量,可以被handle_data使用 40 | context.user_data.window_short = 5 41 | # 计算长线所需的历史bar数目,用户自定义的变量,可以被handle_data使用 42 | context.user_data.window_long = 20 43 | # 入场线, 用户自定义的变量,可以被handle_data使用 44 | context.user_data.enter_threshold = 0.00 45 | # 出场线, 用户自定义的变量,可以被handle_data使用 46 | context.user_data.exit_threshold = 0.00 47 | 48 | 49 | # 阅读3,策略核心逻辑: 50 | # handle_data函数定义了策略的执行逻辑,按照frequency生成的bar依次读取并执行策略逻辑,直至程序结束。 51 | # handle_data和bar的详细说明,请参考新手学堂的解释文档。 52 | def handle_data(context): 53 | # 获取历史数据, 取后window_long根bar 54 | hist = context.data.get_price(context.security, count=context.user_data.window_long, frequency=context.frequency) 55 | if len(hist.index) < context.user_data.window_long: 56 | context.log.warn("bar的数量不足, 等待下一根bar...") 57 | return 58 | # 计算短均线值 59 | short_mean = np.mean(hist['close'][-1 * context.user_data.window_short:]) 60 | # 计算长均线值 61 | long_mean = np.mean(hist['close'][-1 * context.user_data.window_long:]) 62 | 63 | # 价格上轨 64 | upper = long_mean + context.user_data.enter_threshold * long_mean 65 | # 价格下轨 66 | lower = long_mean - context.user_data.exit_threshold * long_mean 67 | 68 | context.log.info('当前 短期均线 = %s, 长期均线 = %s, 上轨 = %s, 下轨 = %s' % (short_mean, long_mean, upper, lower)) 69 | 70 | # 短期线突破长期线一定比例,产生买入信号 71 | if short_mean > upper: 72 | if context.account.huobi_cny_cash > 0: 73 | # 有买入信号,且持有现金,则市价单全仓买入 74 | context.log.info("短期均线穿越上轨,产生买入信号") 75 | context.log.info("正在买入 %s" % context.security) 76 | context.log.info("下单金额为 %s 元" % context.account.huobi_cny_cash) 77 | context.order.buy(context.security, cash_amount=str(context.account.huobi_cny_cash)) 78 | # 短期线低于长期线一定比例,产生卖出信号 79 | elif short_mean < lower: 80 | if getattr(context.account, context.security) > 0: 81 | # 有卖出信号,且持有仓位,则市价单全仓卖出 82 | context.log.info("短期均线穿越下轨,产生卖出信号") 83 | context.log.info("正在卖出 %s" % context.security) 84 | context.log.info("卖出数量为 %s" % getattr(context.account, context.security)) 85 | context.order.sell(context.security, quantity=str(getattr(context.account, context.security))) 86 | else: 87 | context.log.info('无交易信号,进入下一根bar') 88 | -------------------------------------------------------------------------------- /userStrategy/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wequant-org/liveStrategyEngine/6e799e33cfa83b4496dd694e1d2bf30ea104e2c1/utils/__init__.py -------------------------------------------------------------------------------- /utils/errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | class StartRunningTimeEmptyError(ValueError): 6 | pass 7 | 8 | 9 | class InvalidFilterError(ValueError): 10 | pass 11 | 12 | 13 | class TypeError(ValueError): 14 | pass 15 | 16 | 17 | class EmptySecurityError(ValueError): 18 | pass 19 | 20 | 21 | class InvalidSecurityError(ValueError): 22 | pass 23 | 24 | 25 | class InvalidFrequencyError(ValueError): 26 | pass 27 | -------------------------------------------------------------------------------- /utils/helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import datetime 5 | import io 6 | import logging 7 | import math 8 | import sys 9 | import time 10 | import traceback 11 | import uuid 12 | 13 | COIN_TYPE_BTC_CNY = "btc_cny" 14 | COIN_TYPE_LTC_CNY = "ltc_cny" 15 | HUOBI_COIN_TYPE_BTC = 1 16 | HUOBI_COIN_TYPE_LTC = 2 17 | COIN_TYPE_CNY = "cny" 18 | COIN_TYPE_USD = "usd" 19 | 20 | coinTypeStructure = { 21 | COIN_TYPE_BTC_CNY: { 22 | "huobi": { 23 | "coin_type": HUOBI_COIN_TYPE_BTC, 24 | "market": COIN_TYPE_CNY, 25 | "coin_str": "huobi_cny_btc", 26 | "market_str": "huobi_cny_cash" 27 | }, 28 | "okcoin": { 29 | "coin_type": COIN_TYPE_BTC_CNY, 30 | "market": COIN_TYPE_CNY, 31 | "coin_str": "okcoin_cny_btc", 32 | "market_str": "okcoin_cny_cash" 33 | } 34 | }, 35 | COIN_TYPE_LTC_CNY: { 36 | "huobi": { 37 | "coin_type": HUOBI_COIN_TYPE_LTC, 38 | "market": COIN_TYPE_CNY, 39 | "coin_str": "huobi_cny_ltc", 40 | "market_str": "huobi_cny_cash" 41 | }, 42 | "okcoin": { 43 | "coin_type": COIN_TYPE_LTC_CNY, 44 | "market": COIN_TYPE_CNY, 45 | "coin_str": "okcoin_cny_ltc", 46 | "market_str": "okcoin_cny_cash" 47 | } 48 | } 49 | } 50 | 51 | 52 | # 从huobi style的security拿到okcoin style的security 53 | def getCoinMarketTypeFromSecurity(security): 54 | if security == "huobi_cny_btc": 55 | return COIN_TYPE_BTC_CNY 56 | elif security == "huobi_cny_ltc": 57 | return COIN_TYPE_LTC_CNY 58 | else: 59 | raise ValueError("invalid security %s" % security) 60 | 61 | 62 | # 向下取小数点后decimal_places位精度 63 | def downRound(qty, decimal_places=4): 64 | return int(qty * math.pow(10, decimal_places)) / int(math.pow(10, decimal_places)) 65 | 66 | 67 | # 对币数量进行精度裁剪 68 | def getRoundedQuantity(qty, coin_type): 69 | if coin_type == COIN_TYPE_BTC_CNY: 70 | # 按照okcoin的下单规则,比特币都是0.01 btc的整数倍,取下限 71 | return downRound(qty, decimal_places=2) 72 | elif coin_type == COIN_TYPE_LTC_CNY: 73 | # 按照okcoin的下单规则,莱特币都是0.1 ltc的整数倍,取下限 74 | return downRound(qty, decimal_places=1) 75 | else: 76 | raise ValueError("invalid coin type %s" % coin_type) 77 | 78 | 79 | # 从对象拿数据 80 | def componentExtract(object, key, default=None): 81 | if type(object) == dict: 82 | return object.get(key, default) 83 | else: 84 | return getattr(object, key, default) 85 | 86 | 87 | # 获取uuid 88 | def getUUID(): 89 | return str(uuid.uuid1()) 90 | 91 | 92 | # print traceback to log 93 | def printTracebackToLog(timeLog): 94 | try: 95 | output = io.StringIO() 96 | exc_type, exc_value, exc_traceback = sys.exc_info() 97 | traceback.print_tb(exc_traceback, file=output) 98 | timeLog(output.getvalue()) 99 | finally: 100 | output.close() 101 | 102 | 103 | # 获取当前时间,返回字符串,格式为:'YYYYMMDD_hhmmss' 104 | def current_time_str(): 105 | current_time = datetime.datetime.now() 106 | time_string = current_time.strftime('%Y%m%d_%H%M%S') 107 | return time_string 108 | 109 | 110 | # 将时间戳转化为可读时间 111 | def timestamp_to_timestr(timestamp): 112 | time_struct = time.localtime(timestamp) 113 | time_string = time.strftime("%Y%m%d_%H%M%S", time_struct) 114 | return time_string 115 | 116 | 117 | # 抽象出timelogger 118 | class TimeLogger(object): 119 | def __init__(self, logFileName): 120 | self.timeLogger = logging.getLogger('timeLog') 121 | self.timeLogger.setLevel(logging.DEBUG) 122 | self.timeLogHandler = logging.FileHandler(logFileName) 123 | self.timeLogHandler.setLevel(logging.DEBUG) 124 | self.consoleLogHandler = logging.StreamHandler() 125 | self.consoleLogHandler.setLevel(logging.DEBUG) 126 | # 定义handler的输出格式 127 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 128 | self.timeLogHandler.setFormatter(formatter) 129 | self.consoleLogHandler.setFormatter(formatter) 130 | # 给timeLogger添加handler 131 | self.timeLogger.addHandler(self.timeLogHandler) 132 | self.timeLogger.addHandler(self.consoleLogHandler) 133 | 134 | def timeLog(self, content, level=logging.INFO): 135 | if level == logging.DEBUG: 136 | self.timeLogger.debug(content) 137 | elif level == logging.INFO: 138 | self.timeLogger.info(content) 139 | elif level == logging.WARN: 140 | self.timeLogger.warn(content) 141 | elif level == logging.ERROR: 142 | self.timeLogger.error(content) 143 | elif level == logging.CRITICAL: 144 | self.timeLogger.critical(content) 145 | else: 146 | raise ValueError("unsupported logging level %d" % level) 147 | 148 | 149 | # 策略进程 150 | def start_strat(strat): 151 | if strat.dailyExitTime is not None: 152 | # check whether current time is after the dailyExitTime, if yes, exit 153 | while datetime.datetime.now() <= datetime.datetime.strptime( 154 | datetime.date.today().strftime("%Y-%m-%d") + " " + strat.dailyExitTime, 155 | "%Y-%m-%d %H:%M:%S"): 156 | try: 157 | strat.go() 158 | except Exception: 159 | printTracebackToLog(strat.timeLog) 160 | strat.timeLog("抵达每日终结时间:%s, 现在退出." % strat.dailyExitTime) 161 | else: 162 | while True: 163 | try: 164 | strat.go() 165 | except Exception: 166 | printTracebackToLog(strat.timeLog) 167 | --------------------------------------------------------------------------------