def sync_orders(self, symbol): orders = self.get_open_orders(symbol) if not orders: return df_amount, df_value = self.__exchange.get_deals(symbol) for order in orders: self.log_debug("order: %r" % order) order_id = order["order_id"] order_amount = order["amount"] if order_id not in df_amount.index: """ 没有成交信息 """ continue deal_amount = df_amount[order_id] target_coin, base_coin = xq.get_symbol_coins(symbol) deal_amount = ts.reserve_float(deal_amount, self.config["digits"][target_coin]) deal_value = df_value[order_id] status = xq.ORDER_STATUS_OPEN if deal_amount > order_amount: self.log_error("最新成交数量(%f)大于委托数量(%f) %g" % (deal_amount, order_amount, (deal_amount - order_amount))) continue elif deal_amount == order_amount: status = xq.ORDER_STATUS_CLOSE else: if deal_amount < order["deal_amount"]: self.log_warning("最新成交数量小于委托里记载的旧成交数量") continue elif deal_amount == order["deal_amount"]: self.log_info("成交数量没有更新") else: pass if self.__exchange.order_status_is_close(symbol, order_id): status = xq.ORDER_STATUS_CLOSE self.log_debug( "deal_amount: %f, deal_value: %g, deal_price: %g" % (deal_amount, deal_value, deal_value / deal_amount)) self.td_db.update_one( DB_ORDERS_NAME, order["_id"], { "deal_amount": deal_amount, "deal_value": deal_value, "status": status, }, ) return
def _get_position_from_orders(self, symbol, orders): cycle_first_order = None history_profit = 0 history_commission = 0 cycle_amount = 0 cycle_value = 0 cycle_commission = 0 target_coin, base_coin = xq.get_symbol_coins(symbol) for order in orders: if not self.check_order(order): return None if order["action"] == xq.OPEN_POSITION: if cycle_amount == 0: cycle_first_order = order cycle_amount += order["deal_amount"] else: cycle_amount -= order["deal_amount"] cycle_amount = ts.reserve_float(cycle_amount, self.config["digits"][target_coin]) cycle_value += self.get_order_value(order) cycle_commission += self.get_order_commission(order) if cycle_amount == 0: history_profit += cycle_value - cycle_commission history_commission += cycle_commission cycle_value = 0 cycle_commission = 0 # 持仓信息 pst_info = { "amount": cycle_amount, # 数量 "value": cycle_value, # 金额 "commission": cycle_commission, # 佣金 "history_profit": history_profit, # 历史利润 "history_commission": history_commission, # 历史佣金 } if cycle_amount > 0: pst_info["price"] = abs(cycle_value) / cycle_amount pst_info["cost_price"] = (abs(cycle_value) + cycle_commission) / cycle_amount pst_info["direction"] = cycle_first_order["direction"] pst_info["start_time"] = datetime.fromtimestamp( cycle_first_order["create_time"]) if "high" in order: pst_info["high"] = cycle_first_order["high"] pst_info["low"] = cycle_first_order["low"] return pst_info
def calc_order(self, symbol, orders): amount = 0 buy_value = 0 sell_value = 0 buy_commission = 0 sell_commission = 0 total_profit = 0 target_coin, base_coin = xq.get_symbol_coins(symbol) for index, order in enumerate(orders): if index == 0: order["cycle_id"] = 0 else: pre_order = orders[index - 1] order["cycle_id"] = pre_order["cycle_id"] if order["cycle_id"] != pre_order["side"] and order[ "side"] == xq.SIDE_BUY: order["cycle_id"] += 1 commission = order["deal_value"] * self.config["commission_rate"] deal_price = order["deal_value"] / order["deal_amount"] if order["side"] == xq.SIDE_BUY: amount += order["deal_amount"] buy_value += order["deal_value"] buy_commission += commission else: amount -= order["deal_amount"] sell_value += order["deal_value"] sell_commission += commission amount = ts.reserve_float(amount, self.config["digits"][target_coin]) buy_cost = buy_value + buy_commission order[ "profit"] = deal_price * amount + sell_value - sell_commission - buy_cost order["profit_rate"] = order["profit"] / buy_cost order["total_profit"] = (total_profit + order["profit"]) order["total_profit_rate"] = order["total_profit"] / self.value if amount == 0: total_profit += order["profit"] buy_value = 0 sell_value = 0 buy_commission = 0 sell_commission = 0 return orders
def stat_orders(self, symbol, orders): cycle_id = 1 history_profit = 0 history_commission = 0 cycle_amount = 0 cycle_value = 0 cycle_commission = 0 target_coin, base_coin = xq.get_symbol_coins(symbol) for order in orders: if not self.check_order(order): return None order["cycle_id"] = cycle_id if order["action"] == xq.OPEN_POSITION: cycle_amount += order["deal_amount"] else: cycle_amount -= order["deal_amount"] cycle_amount = ts.reserve_float(cycle_amount, self.config["digits"][target_coin]) cycle_value += self.get_order_value(order) cycle_commission += self.get_order_commission(order) deal_price = order["deal_value"] / order["deal_amount"] cycle_profit = self.get_floating_profit(order["direction"], cycle_amount, cycle_value, cycle_commission, deal_price) order["floating_profit"] = cycle_profit order["history_profit"] = history_profit order["total_profit"] = cycle_profit + history_profit open_value = self.value if self.config["mode"] == 1: open_value += history_profit order[ "floating_profit_rate"] = order["floating_profit"] / open_value order["history_profit_rate"] = order["history_profit"] / self.value order["total_profit_rate"] = order["total_profit"] / self.value if cycle_amount == 0: history_profit += cycle_profit history_commission += cycle_commission cycle_value = 0 cycle_commission = 0 cycle_id += 1 return orders
def analyze(self, symbol, orders): i = 1 amount = 0 value = 0 commission = 0 target_coin, base_coin = xq.get_symbol_coins(symbol) print( " id create_time side pst_rate cur_price deal_amount deal_value amount value commission profit profit_rate rmk" ) for order in orders: cur_price = order["deal_value"] / order["deal_amount"] if order["side"] == xq.SIDE_BUY: amount += order["deal_amount"] value += order["deal_value"] else: amount -= order["deal_amount"] value -= order["deal_value"] commission += order["deal_value"] * self.config["commission_rate"] amount = ts.reserve_float(amount, self.config["digits"][target_coin]) profit = cur_price * amount - value - commission profit_rate = profit / self.config["limit"]["value"] print( "%4d %s %4s %8g %10g %11g %10g %10g %10g %10g %10g %10.2f%% %s" % ( i, datetime.fromtimestamp(order["create_time"]), order["side"], order["pst_rate"], cur_price, order["deal_amount"], order["deal_value"], amount, value, commission, profit, round(profit_rate * 100, 2), order["rmk"], ) ) i += 1
def _get_position(self, symbol, orders, cur_price): info = { "amount": 0, # 数量 "price": 0, # 平均价格,不包含佣金 "cost_price": 0, # 分摊佣金后的成本价 "value": 0, # 金额 "commission": 0, # 佣金 "profit": 0, # 当前利润 "history_profit": 0, # 历史利润 "history_commission": 0, # 历史佣金 "start_time": None, # 本周期第一笔买入时间 "pst_rate": 0, # 本次交易完成后要达到的持仓率 } target_coin, base_coin = xq.get_symbol_coins(symbol) for order in orders: deal_amount = order["deal_amount"] deal_value = order["deal_value"] commission = deal_value * self.config["commission_rate"] if order["side"] == xq.SIDE_BUY: if info["amount"] == 0: info["start_time"] = datetime.fromtimestamp(order["create_time"]) info["amount"] += deal_amount info["value"] += deal_value info["commission"] += commission elif order["side"] == xq.SIDE_SELL: info["amount"] -= deal_amount info["value"] -= deal_value info["commission"] += commission else: logging.error("错误的委托方向") continue info["amount"] = ts.reserve_float(info["amount"], self.config["digits"][target_coin]) if info["amount"] == 0: info["history_profit"] -= info["value"] + info["commission"] info["history_commission"] += info["commission"] info["value"] = 0 info["commission"] = 0 info["start_time"] = None if info["amount"] == 0: pass elif info["amount"] > 0: info["profit"] = ( cur_price * info["amount"] - info["value"] - info["commission"] ) info["price"] = info["value"] / info["amount"] info["cost_price"] = (info["value"] + info["commission"]) / info["amount"] else: logging.error("持仓数量不可能小于0") info["limit_base_amount"] = self.config["limit"]["value"] if orders: info["pst_rate"] = orders[-1]["pst_rate"] logging.info( "symbol( %s ); current price( %g ); position(%s%s%s history_profit: %g, history_commission: %g, total_profit_rate: %g)", symbol, cur_price, "amount: %g, price: %g, cost price: %g, value: %g, commission: %g, limit: %g, profit: %g," % ( info["amount"], info["price"], info["cost_price"], info["value"], info["commission"], info["limit_base_amount"], info["profit"], ) if info["amount"] else "", " profit rate: %g," % (info["profit"] / (info["value"] + info["commission"])) if info["value"] else "", " start_time: %s\n," % info["start_time"].strftime("%Y-%m-%d %H:%M:%S") if info["start_time"] else "", info["history_profit"], info["history_commission"], (info["profit"] + info["history_profit"]) / info["limit_base_amount"], ) # print(info) return info
def handle_order(self, symbol, cur_price, check_signals): """ 处理委托 """ position_info = self.get_position(symbol, cur_price) rc_signals = self.risk_control(position_info, cur_price) if xq.SIDE_BUY in rc_signals: logging.warning("风控方向不能为买") return signals = rc_signals + check_signals if not signals: return logging.info("signals(%r)", signals) dcs_side, dcs_pst_rate, dcs_rmk, dcs_cba = xq.decision_signals2(signals) logging.info( "decision signal side(%s), position rate(%g), rmk(%s), can buy after(%s)", dcs_side, dcs_pst_rate, dcs_rmk, dcs_cba, ) if dcs_side is None: return if self.can_buy_time: if self.now() < self.can_buy_time: # 时间范围之内,只能卖,不能买 if dcs_side != xq.SIDE_SELL: return else: # 时间范围之外,恢复 self.can_buy_time = None if dcs_cba: if not self.can_buy_time or (self.can_buy_time and self.can_buy_time < self.now() + dcs_cba): self.can_buy_time = self.now() + dcs_cba logging.info("can buy time: %s", self.can_buy_time) if dcs_pst_rate > 1 or dcs_pst_rate < 0: logging.warning("仓位率(%g)超出范围(0 ~ 1)", dcs_pst_rate) return limit_mode = self.config["limit"]["mode"] limit_value = self.config["limit"]["value"] if limit_mode == 0: pass elif limit_mode == 1: limit_value += position_info["history_profit"] else: logging("请选择额度模式,默认是0") target_coin, base_coin = xq.get_symbol_coins(symbol) if dcs_side == xq.SIDE_BUY: if position_info["pst_rate"] >= dcs_pst_rate: return buy_base_amount = ( limit_value * dcs_pst_rate - position_info["value"] - position_info["commission"] ) if buy_base_amount <= 0: return base_balance = self.get_balances(base_coin) logging.info("base balance: %s", base_balance) buy_base_amount = min(xq.get_balance_free(base_balance), buy_base_amount) logging.info("buy_base_amount: %g", buy_base_amount) if buy_base_amount <= 0: # return target_amount = ts.reserve_float( buy_base_amount / (cur_price * (1 + self.config["commission_rate"])), self.config["digits"][target_coin], ) rate = 1.1 elif dcs_side == xq.SIDE_SELL: if position_info["pst_rate"] <= dcs_pst_rate: return target_amount = ts.reserve_float( position_info["amount"] * (position_info["pst_rate"] - dcs_pst_rate) / position_info["pst_rate"], self.config["digits"][target_coin], ) rate = 0.9 else: return logging.info("%s target amount: %g" % (dcs_side, target_amount)) if target_amount <= 0: return limit_price = ts.reserve_float(cur_price * rate, self.config["digits"][base_coin]) order_id = self.send_order_limit( dcs_side, symbol, dcs_pst_rate, cur_price, limit_price, target_amount, "%s, timedelta: %s, can buy after: %s" % (dcs_rmk, dcs_cba, self.can_buy_time) if (dcs_cba or self.can_buy_time) else "%s" % (dcs_rmk), ) logging.info( "current price: %g; rate: %g; order_id: %s", cur_price, rate, order_id )
def APO(klines_df, timeperiod=14): real = talib.APO(klines_df["close"], fastperiod=12, slowperiod=26, matype=0) return [ts.reserve_float(a, 6) for a in real]
def STOCHRSI(klines_df, timeperiod=14): fastk, fastd = talib.STOCHRSI(klines_df["close"], timeperiod=14, fastk_period=5, fastd_period=3, fastd_matype=0) return [ts.reserve_float(a, 6) for a in fastk], [ts.reserve_float(a, 6) for a in fastd]
def WILLR(klines_df, timeperiod=14): real = talib.WILLR(klines_df["high"], klines_df["low"], klines_df["close"], timeperiod=14) return [ts.reserve_float(a, 6) for a in real]
def TRIX(klines_df, timeperiod=30): real = talib.TRIX(klines_df["close"], timeperiod=timeperiod) return [ts.reserve_float(a, 6) for a in real]
def MINUS_DM(klines_df, timeperiod=14): real = talib.MINUS_DM(klines_df["high"], klines_df["low"], timeperiod=14) return [ts.reserve_float(a, 6) for a in real]
def send_order(self, symbol, position_info, cur_price, direction, action, pst_rate, stop_loss_price, rmk): limit_price_rate = self.config["limit_price_rate"] limit_mode = self.config["mode"] limit_value = self.value if limit_mode == 0: pass elif limit_mode == 1: limit_value += position_info["history_profit"] else: self.log_error("请选择额度模式,默认是0") target_coin, base_coin = xq.get_symbol_coins(symbol) if action == bl.OPEN_POSITION: # 开仓 if "pst_rate" in position_info and position_info[ "pst_rate"] >= pst_rate: return if position_info[POSITON_AMOUNT_KEY] == 0: pst_cost = 0 else: pst_cost = abs( position_info["value"]) + position_info["commission"] base_amount = limit_value * pst_rate - pst_cost if base_amount <= 0: return if direction == bl.DIRECTION_LONG: # 做多开仓 ''' base_balance = self.get_balances(base_coin) self.log_info("base balance: %s" % base_balance) base_amount = min(xq.get_balance_free(base_balance), base_amount) ''' self.log_info("base_amount: %g" % base_amount) if base_amount <= 0: # return target_amount = base_amount / ( cur_price * (1 + self.config["commission_rate"])) rate = 1 + limit_price_rate["open"] else: # 做空开仓 ''' target_balance = self.get_balances(target_coin) self.log_info("target balance: %s" % target_balance) # target_amount = min(xq.get_balance_free(target_balance), base_amount / cur_price) ''' target_amount = base_amount / cur_price self.log_info("target_amount: %g" % target_amount) if target_amount <= 0: # return rate = 1 - limit_price_rate["open"] else: # 平仓 if (not "pst_rate" in position_info) or position_info["pst_rate"] <= pst_rate: return target_amount = abs(position_info["amount"]) * ( position_info["pst_rate"] - pst_rate) / position_info["pst_rate"] if direction == bl.DIRECTION_LONG: # 做多平仓 rate = 1 - limit_price_rate["close"] else: # 做空平仓 rate = 1 + limit_price_rate["close"] target_amount = ts.reserve_float(target_amount, self.config["digits"][target_coin]) self.log_info("%s %s target amount: %g" % (direction, action, target_amount)) if target_amount <= 0: return limit_price = ts.reserve_float(cur_price * rate, self.config["digits"][base_coin]) order_id = self.send_order_limit( direction, action, symbol, pst_rate, cur_price, limit_price, target_amount, stop_loss_price, rmk, ) self.log_info("current price: %g; rate: %g; order_id: %s" % (cur_price, rate, order_id))
def send_order(self, symbol, position_info, cur_price, direction, action, pst_rate, stop_loss_price, rmk): limit_price_rate = self.config["limit_price_rate"] limit_mode = self.config["mode"] limit_value = self.value if limit_mode == 0: pass elif limit_mode == 1: limit_value += position_info["history_profit"] else: self.log_error("请选择额度模式,默认是0") limit_rate = limit_price_rate["default"] target_coin, base_coin = xq.get_symbol_coins(symbol) if action == bl.OPEN_POSITION: # 开仓 if pst_is_lock(position_info): # 需要解锁动作 if True: # 同一账户 # 实际持仓已经为空,需发起1笔委托来新增持仓 action = bl.UNLOCK_POSITION target_amount = position_info["lock_amount"] else: # 不同账户 # 暂不实现 return else: if "pst_rate" in position_info and position_info[ "pst_rate"] >= pst_rate: return if position_info[POSITON_AMOUNT_KEY] == 0: pst_cost = 0 else: pst_cost = abs( position_info["value"]) + position_info["commission"] base_amount = limit_value * pst_rate - pst_cost if base_amount <= 0: return if direction == bl.DIRECTION_LONG: # 做多开仓 ''' base_balance = self.get_balances(base_coin) self.log_info("base balance: %s" % base_balance) base_amount = min(xq.get_balance_free(base_balance), base_amount) ''' self.log_info("base_amount: %g" % base_amount) if base_amount <= 0: # return target_amount = base_amount / ( cur_price * (1 + self.config["commission_rate"])) rate = 1 + limit_price_rate["open"] else: # 做空开仓 ''' target_balance = self.get_balances(target_coin) self.log_info("target balance: %s" % target_balance) # target_amount = min(xq.get_balance_free(target_balance), base_amount / cur_price) ''' target_amount = base_amount / cur_price self.log_info("target_amount: %g" % target_amount) if target_amount <= 0: # return rate = 1 - limit_price_rate["open"] elif action == bl.CLOSE_POSITION: # 平仓 if pst_is_lock(position_info): # 需要解锁和平仓2个动作 if True: # 同一账户 # 实际持仓已经为空,不需发起委托,但本次持仓结束 self.set_pst_lock_to_close(symbol, rmk) return else: # 不同账户 # 需要发起2笔委托,暂不实现 return else: if (not "pst_rate" in position_info ) or position_info["pst_rate"] <= pst_rate: return target_amount = abs(position_info["amount"]) * ( position_info["pst_rate"] - pst_rate) / position_info["pst_rate"] if direction == bl.DIRECTION_LONG: # 做多平仓 rate = 1 - limit_price_rate["close"] else: # 做空平仓 rate = 1 + limit_price_rate["close"] elif action == bl.LOCK_POSITION: # 锁仓 if pst_is_lock(position_info): # 已经锁仓 return if (not "pst_rate" in position_info ) or position_info["pst_rate"] <= 0: # 没有仓位 return target_amount = position_info[POSITON_AMOUNT_KEY] if direction == bl.DIRECTION_LONG: rate = 1 - limit_rate else: rate = 1 + limit_rate elif action == bl.UNLOCK_POSITION: # 解锁仓 if not pst_is_lock(position_info): # 没有锁仓 return target_amount = position_info[LOCK_POSITON_AMOUNT_KEY] if direction == bl.DIRECTION_LONG: rate = 1 + limit_rate else: rate = 1 - limit_rate else: return target_amount = ts.reserve_float(target_amount, self.config["digits"][target_coin]) self.log_info("%s %s target amount: %g" % (direction, action, target_amount)) if target_amount <= 0: return limit_price = ts.reserve_float(cur_price * rate, self.config["digits"][base_coin]) order_id = self.send_order_limit( direction, action, symbol, pst_rate, cur_price, limit_price, target_amount, stop_loss_price, rmk, ) self.log_info("current price: %g; rate: %g; order_id: %s" % (cur_price, rate, order_id))
def handle_order(self, symbol, position_info, cur_price, check_signals): """ 处理委托 """ rc_signals = self.risk_control(position_info, cur_price) signals = rc_signals + check_signals if not signals: return for signal in signals: self.log_info("signal(%r)" % signal["describe"]) ds_signal = xq.decision_signals(signals) self.log_info( "decision signal (%s %s), position rate(%g), describe(%s), can buy after(%s)" % (ds_signal["direction"], ds_signal["action"], ds_signal["pst_rate"], ds_signal["describe"], ds_signal["can_open_time"])) if ds_signal["action"] is None: return if self.can_open_time: if self.now() < self.can_open_time: # 限定的时间范围内,只能平仓,不能开仓 if ds_signal["action"] != xq.CLOSE_POSITION: return else: # 时间范围之外,恢复 self.can_open_time = None if ds_signal["can_open_time"]: if not self.can_open_time or (self.can_open_time and self.can_open_time < self.now() + ds_signal["can_open_time"]): self.can_open_time = self.now() + ds_signal["can_open_time"] self.log_info("can buy time: %s" % self.can_open_time) if ds_signal["pst_rate"] > 1 or ds_signal["pst_rate"] < 0: self.log_warning("仓位率(%g)超出范围(0 ~ 1)" % ds_signal["pst_rate"]) return limit_price_rate = self.config["limit_price_rate"] limit_mode = self.config["mode"] limit_value = self.value if limit_mode == 0: pass elif limit_mode == 1: limit_value += position_info["history_profit"] else: self.log_error("请选择额度模式,默认是0") target_coin, base_coin = xq.get_symbol_coins(symbol) if ds_signal["action"] == xq.OPEN_POSITION: # 开仓 if "pst_rate" in position_info and position_info[ "pst_rate"] >= ds_signal["pst_rate"]: return pst_cost = abs( position_info["value"]) + position_info["commission"] base_amount = limit_value * ds_signal["pst_rate"] - pst_cost if base_amount <= 0: return if ds_signal["direction"] == xq.DIRECTION_LONG: # 做多开仓 base_balance = self.get_balances(base_coin) self.log_info("base balance: %s" % base_balance) base_amount = min(xq.get_balance_free(base_balance), base_amount) self.log_info("base_amount: %g" % base_amount) if base_amount <= 0: # return target_amount = base_amount / ( cur_price * (1 + self.config["commission_rate"])) rate = 1 + limit_price_rate["open"] else: # 做空开仓 target_balance = self.get_balances(target_coin) self.log_info("target balance: %s" % target_balance) target_amount = min(xq.get_balance_free(target_balance), base_amount / cur_price) self.log_info("target_amount: %g" % target_amount) if target_amount <= 0: # return rate = 1 - limit_price_rate["open"] else: # 平仓 if (not "pst_rate" in position_info ) or position_info["pst_rate"] <= ds_signal["pst_rate"]: return target_amount = abs(position_info["amount"]) * ( position_info["pst_rate"] - ds_signal["pst_rate"]) / position_info["pst_rate"] if ds_signal["direction"] == xq.DIRECTION_LONG: # 做多平仓 rate = 1 - limit_price_rate["close"] else: # 做空平仓 rate = 1 + limit_price_rate["close"] target_amount = ts.reserve_float(target_amount, self.config["digits"][target_coin]) self.log_info( "%s %s target amount: %g" % (ds_signal["direction"], ds_signal["action"], target_amount)) if target_amount <= 0: return limit_price = ts.reserve_float(cur_price * rate, self.config["digits"][base_coin]) order_rmk = ds_signal["describe"] + ": " + ds_signal["rmk"] order_id = self.send_order_limit( ds_signal["direction"], ds_signal["action"], symbol, ds_signal["pst_rate"], cur_price, limit_price, target_amount, "%s, timedelta: %s, can buy after: %s" % (order_rmk, ds_signal["can_open_time"], self.can_open_time) if (ds_signal["can_open_time"] or self.can_open_time) else "%s" % (order_rmk), ) self.log_info("current price: %g; rate: %g; order_id: %s" % (cur_price, rate, order_id))