Example #1
0
async def calcul_balance(account_manager):
    """
    计算做市商账户balance信息的函数:
    生成一个dataframe,包含各个账户的cash(ETH)和山寨币库存情况
    
    balance = await calcul_balance(account_manager)
    Input:
    account_manager:账户管理类
    Output:
    balance:做市商账户组的账户信息dataframe
    """
    # 登录账户并读取数据
    logger.info('"msg": "calcul_balance"')
    try:
        await account_manager.login()
    except:
        logger.error('"error": "Account Login Timeout!"')
        raise

    total_balance = await account_manager.total_balance()
    logger.info('"msg": "read balance done"')

    InventorySymbol = symbol.split('/')[0]  # 山寨币的symbol

    balance = pd.DataFrame(total_balance).T
    balance['cash_total'] = balance['ETH'].apply(lambda x: float(x['total']))
    balance['cash_free'] = balance['ETH'].apply(lambda x: float(x['free']))
    balance['Inventory_total'] = balance[InventorySymbol].apply(
        lambda x: float(x['total']))
    balance['Inventory_free'] = balance[InventorySymbol].apply(
        lambda x: float(x['free']))
    balance = balance[[
        'cash_total', 'cash_free', 'Inventory_total', 'Inventory_free'
    ]]
    return balance
Example #2
0
def calcul_result(OBdf, OB_RealTime, BestPrice_Master, Fairvalue):
    """
    计算Result的函数:
    如果当前的中间价低于(Fairvalue-阈值),则Result=1,表明需要向上重置orderbook
    如果当前的中间价高于(Fairvalue+阈值),则Result=-1,表明需要向下重置orderbook
    其余情况,Result=0
    其中,中间价的计算需要注意,我们会忽略那些在Master最近一次挂的(bid1*,ask1*)区间内的价格,认为这些挂单不影响Fairvalue的判断;
    也就是说,如果最新的bid1_RT满足bid1*<bid1_RT<ask1*,则强制让bid1=bid1*,其余则是一般情形所认为的bid1=bid1_RT(认为挂在区间内的单子是噪声)
    同样的,如果最新的ask1_RT满足bid1*<ask1_RT<ask1*,则强制让ask1=ask1*,其余则是一般情形所认为的ask1=ask1_RT
    
    Result = calcul_result(OBdf,OB_RealTime,BestPrice_Master,Fairvalue)
    Input:
    OBdf:处理后的历史orderbook数据(5分钟采样)
    OB_RealTime:当前实时的ask1/bid1数据
    BestPrice_Master:最近一次Master挂的ask1/bid1的价格
    Fairvalue:公平的中间价
    Output:当前实时的ask1/bid1数据
    Result:结果变量
    """
    # 计算调整的dex实时中间价midP_RT_Adjust(依据实时ask1/bid1和最近一次Master挂出来的ask1/bid1相对关系来调整)
    ask1_Master = BestPrice_Master.to_dataframe()['ask1'][0]
    bid1_Master = BestPrice_Master.to_dataframe()['bid1'][0]
    ask1_RT = OB_RealTime['ask1'][0]
    bid1_RT = OB_RealTime['bid1'][0]
    if (bid1_Master < bid1_RT and bid1_RT < ask1_Master):
        bid1 = bid1_Master
    else:
        bid1 = bid1_RT
    if (bid1_Master < ask1_RT and ask1_RT < ask1_Master):
        ask1 = ask1_Master
    else:
        ask1 = ask1_RT
    midP_RT_Adjust = round((ask1 + bid1) / 2, 8)
    logger.info(
        f'"data": "Ajusted dex:[{symbol}]-ask1:{round(ask1,8)}-bid1:{round(bid1,8)}-midP_Adjust:{round(midP_RT_Adjust,8)}"'
    )
    # dex_benchmark价差的历史标准差
    OBdf['midPDiff'] = (OBdf['ask1'] + OBdf['bid1'] - OBdf['ask1_BM'] -
                        OBdf['bid1_BM']) / 2
    HisStd = OBdf['midPDiff'].std()
    HisStdT = HisStd * StdCo  # 乘以一个给定的标准差系数,得到判断阈值
    logger.info(
        f'"data": "IF UPDATE ORDERBOOK?--Current Adjusted midPrice:{round(midP_RT_Adjust,8)}: LowBound:{round(Fairvalue-HisStdT,8)}--Fairvalue:{round(Fairvalue,8)}--UpBound:{round(Fairvalue+HisStdT,8)}"'
    )
    # 判断条件Result
    if midP_RT_Adjust >= (Fairvalue + HisStdT):
        Result = -1
    elif midP_RT_Adjust <= (Fairvalue - HisStdT):
        Result = 1
    else:
        Result = 0
    return Result
Example #3
0
def data_transform_RealTime(msg, msg_BM):
    """
    处理当前实时数据,只有benchmark的值可能转换
    
    OB_RealTime = data_transform_RealTime(msg,msg_BM)
    Input:
    msg:dex的实时数据
    msg_BM:bemchmark交易所的实时数据
    Ouput:
    OB_RealTime:一个dataframe,根据条件对benchmark的当前价格转换,然后获得的一个ask1、bid1、ask1_BM、bid1_BM四列数的表
    """
    # --benchmark数据--
    ordertime_BM = msg_BM.get("datetime")
    symbol_BM = msg_BM.get("symbol")
    ask1_BM = msg_BM.get("asks")[0][0]
    bid1_BM = msg_BM.get("bids")[0][0]
    # 以ETH/USDT作为benchmark,需要将ETH/USDT转换为一个模拟价格:(1-b)*山寨币初始价格+(山寨币初始价格*b/ETH初始价格)*ETH价格
    ETH2BMPrice = b * Price0 / ETH0  # 转换系数
    if IfUsingETHasBM == 1:
        ask1_BM = (1 - b) * Price0 + ETH2BMPrice * ask1_BM
        bid1_BM = (1 - b) * Price0 + ETH2BMPrice * bid1_BM
    midP_BM = (ask1_BM + bid1_BM) / 2
    # --dex数据--
    ordertime = msg.get("datetime")
    symbol = msg.get("symbol")
    ask1 = msg.get("asks")[0][0]
    bid1 = msg.get("bids")[0][0]
    midP = (ask1 + bid1) / 2
    logger.info(
        f'"data": "dex:[{ordertime}][{symbol}]-ask1:{round(ask1,8)}-bid1:{round(bid1,8)}-midP:{round(midP,8)}"'
    )
    logger.info(
        f'"data": "benchmark:[{ordertime_BM}][{symbol_BM}]-ask1:{round(ask1_BM,8)}-bid1:{round(bid1_BM,8)}-midP:{round(midP_BM,8)}"'
    )
    # --合并生成一个dataframe--
    x = datetime.datetime.utcfromtimestamp(msg.get("nonce"))
    y = x.strftime("%Y-%m-%d %H:%M:%S")
    UTCordertime = datetime.datetime.strptime(y, "%Y-%m-%d %H:%M:%S")
    OB_RealTime = pd.DataFrame(
        {
            'ask1': ask1,
            'bid1': bid1,
            'ask1_BM': ask1_BM,
            'bid1_BM': bid1_BM
        },
        index=[UTCordertime])
    return OB_RealTime
Example #4
0
def calcul_fairvalue(balance, InventoryWant2be, OBdf, OB_RealTime):
    """
    计算fairvalue的函数:
    fairvalue = benchmark中间价 + 价差的移动均值 + 库存调整项Adjust1 + 预测调整项Adjust2
    其中:
    Adjust1 = ((与目标库存的差)*(benchmark中间价)/激进系数)*(中间价的1%)
    
    Fairvalue = calcul_fairvalue(balance,InventoryWant2be,OBdf,OB_RealTime)
    Input:
    balance:做市商账户的持仓情况
    InventoryWant2be:依据自成交情况更新的山寨币合宜库存
    OBdf:处理后的历史orderbook数据(5分钟采样)
    OB_RealTime:当前实时的ask1/bid1数据
    Output:
    Fairvalue:公平的中间价
    """
    # benchmark的中间价
    midP_BM_RT = float((OB_RealTime['ask1_BM'] + OB_RealTime['bid1_BM']) / 2)
    # 价差的移动均值
    OBdf['midPDiff'] = (OBdf['ask1'] + OBdf['bid1'] - OBdf['ask1_BM'] -
                        OBdf['bid1_BM']) / 2
    HisMean = OBdf['midPDiff'].mean()
    # 库存调整项
    InventorySum = balance['Inventory_total'].sum()
    Adjust1 = -((InventorySum - InventoryWant2be) * midP_BM_RT /
                InventoryAdjustCo) * (1e-2 * midP_BM_RT)
    logger.info(
        f'"data": "InventoryWant2be:{InventoryWant2be}--InventorySum:{InventorySum}--InventoryDiff(ETH):{(InventorySum-InventoryWant2be)*midP_BM_RT}"'
    )
    # 预测调整项
    Adjust2 = AlphaAdjustConstant
    Fairvalue = midP_BM_RT + HisMean + Adjust1 + Adjust2
    logger.info(
        f'"data": "Fairvalue({round(Fairvalue,8)}) = BenchmarkPrice({round(midP_BM_RT,8)}) + MA_PDiff({round(1e8*HisMean,2)}*1e-8) + Adjust1({round(Adjust1,8)}) + Adjust2({round(Adjust2,8)})"'
    )
    return Fairvalue
Example #5
0
def calcul_spread(OBdf, OB_RealTime):
    """
    计算spread的函数:
    (1) 如果不用ETH/USDT作为benchmark(其他交易所有活跃品质),则用benchmark交易所的spread(max(当前,历史)),乘以一个扩大比例系数;
    (2) 如果用ETH/USDT作为benchmark,则benchmark的spread不能用了(因为ETH/USDT很活跃,spread很小),则用自己的历史spread
    
    spread = calcul_spread(OBdf)
    Input:
    OBdf:处理后的历史orderbook数据(5分钟采样)
    OB_RealTime:当前实时的ask1/bid1数据
    Output:
    spread:最优买卖的价差
    """
    spread_Mean = (OBdf['ask1'] - OBdf['bid1']).mean()
    spread_BM_RT_Mean = (OB_RealTime['ask1_BM'][0] - OB_RealTime['bid1_BM'][0])
    spread_BM_Mean = (OBdf['ask1_BM'] - OBdf['bid1_BM']).mean()
    # 依据条件判断用benchmark的spread还是自己的历史spread
    if IfUsingETHasBM == 0:
        spread = max(spread_BM_RT_Mean, spread_BM_Mean) * spread2BM
    else:
        spread = spread_Mean

    logger.info(f'"data": "Set Best bid/ask Spread: {spread}"')
    return spread
Example #6
0
async def deal_with_data(msg, msg_BM, BestPrice_Master, cache, account_manager,
                         loop):
    logger.info('"msg": "======start get and manufacture data======"')
    # ----当前的实时数据的处理----
    func1 = partial(data_transform_RealTime, msg, msg_BM)
    OB_RealTime = await loop.run_in_executor(None, func1)  # 计算部分放到多进程执行器中执行
    # ----历史数据的处理----
    func2 = partial(data_transform_His, cache)
    OBdf = await loop.run_in_executor(None, func2)
    # ----做市商账户信息----
    balance = await calcul_balance(account_manager)
    logger.info(f'"data": "get balance {balance}"')
    # ----计算spread----
    logger.info('"msg": "======start calculate Key Data======"')
    func3 = partial(calcul_spread, OBdf, OB_RealTime)
    spread = await loop.run_in_executor(None, func3)
    # ----计算Fairvalue----
    func4 = partial(calcul_fairvalue, balance, InventoryWant2be, OBdf,
                    OB_RealTime)
    Fairvalue = await loop.run_in_executor(None, func4)
    # ----计算判断条件Result----
    func5 = partial(calcul_result, OBdf, OB_RealTime, BestPrice_Master,
                    Fairvalue)
    Result = await loop.run_in_executor(None, func5)
    # ----根据更新条件和orderbook更新是否正常的状态,判断是否需要更新orderbook----
    global ISOrderbookUpdateNotOK
    if (Result != 0 or ISOrderbookUpdateNotOK == 1):  # 满足更新条件或orderbook更新不正常时
        logger.info('"msg": "====orderbook upate===="')
        # ----依据Result结果,计算orderbook,并发送订单----
        func6 = partial(calcul_orderbook, Fairvalue, spread, OB_RealTime)
        orderbook = await loop.run_in_executor(None, func6)
        logger.info('"msg": "orderbook done!"')
        # ----发送订单----
        await send_order(account_manager, balance, orderbook)
        logger.info('"msg": "send_order done!"')
        logger.info(f'"data": "***orderbook is: {str(orderbook)}***"')
    else:
        logger.info('"msg": "====no orderbook update===="')
        # ----不更新订单簿的时候,进行自成交相关操作----
        global orderbookUpdateCount
        # 不自成交时,orderUpdateCount随机加0或1
        if orderbookUpdateCount < SelfTradeFreq:
            orderbookUpdateCount += int((round(np.random.uniform(0, 1))))
            logger.info(
                f'"data": "--No SelfTradeOccurs: After About {SelfTradeFreq - orderbookUpdateCount + 0.5}min will Occour!"'
            )
        # 自成交时,orderUodateCount变回0
        else:
            await SelfTrade(account_manager, balance, Fairvalue, spread,
                            OB_RealTime)
            orderbookUpdateCount = 0

    # ----向缓存append数据----
    data_append(msg, msg_BM)
Example #7
0
async def SelfTrade(account_manager, balance, Fairvalue, spread, OB_RealTime):
    """
    处理自成交的函数:
    SelfTrade(account_manager, balance, Fairvalue, spread, OB_RealTime)
    调用这个函数,就会完成一次自成交操作
    
    Input:
    account_manager:账户管理的类
    balance:账户信息的dataframe
    Fairvalue:之前计算好的公平的中间价
    spread:之前计算好的最优买卖价差
    OB_RealTime:当前实时的ask1/bid1数据
    """
    # ----登录账户----
    try:
        await account_manager.login()
    except:
        logger.error('"error": "Account Login Timeout!"')
        raise
    # ----信息预处理----
    IfSelfTrade = 1  # 是否自成交的判断变量
    # 首先做一个判断,如果Fairvalue与当前的中间价差距超过max(2%, 2*spreadRatio_orderbook),则强制不产生自成交
    ask1_RT = OB_RealTime['ask1'][0]
    bid1_RT = OB_RealTime['bid1'][0]
    midP_orderbook = Fairvalue
    midP_RT = (ask1_RT + bid1_RT) / 2
    spreadRatio_orderbook = spread / Fairvalue
    if abs(midP_orderbook / midP_RT - 1) >= max(2 * 1e-2,
                                                2 * spreadRatio_orderbook):
        IfSelfTrade = 0  # 将IfSelfTrade设为0,即强制不进行自成交
        logger.info(
            f'"data": "No SelfTrade, Because Gap Too Large -- Market RealTime midP: {midP_RT} -- Orderbook midP: {midP_orderbook}"'
        )
    # balance1,所有slave账户按照库存总额排序,以此确定用哪个slave来成交
    balance1 = balance.sort_values('Inventory_total').reset_index()
    balance1 = balance1[balance1['index'] != 'Master']
    Slave_Cash_Total = balance1['cash_total'].sum()
    Slave_Inventory_Total = balance1['Inventory_total'].sum() * midP_orderbook
    Master_Cash_free = balance[balance.index == 'Master']['cash_free'][0]
    Master_Inventory_free = balance[
        balance.index == 'Master']['Inventory_free'][0] * midP_orderbook
    # 以slave账户总库存来确定自成交的方向--slave账户总山寨币库存低则slave买入,总现金库存低则slave卖出,其余情况随机
    # 以master账户可用来确定自成交方向--master账户山寨币可用低则slave卖出,现金可用低则slave买入,其余情况随机
    if (Slave_Cash_Total < Slave_Cash_Total_MIN) or (
            Master_Inventory_free < Master_Inventory_free_MIN):
        Direction = -1
    elif (Slave_Inventory_Total < Slave_Inventory_Total_MIN) or (
            Master_Cash_free < Master_Cash_free_MIN):
        Direction = 1
    else:
        Direction = int(round(np.random.uniform(0, 1), 0)) * 2 - 1
    logger.info(f'"data": "SelfTrade Direction: {Direction}"')
    logger.info(
        f'"data": "-- Slave_Cash_Total: {Slave_Cash_Total} -- Slave_Inventory_Total: {Slave_Inventory_Total}"'
    )
    logger.info(
        f'"data": "-- Master_Cash_free: {Master_Cash_free} -- Master_Inventory_free: {Master_Inventory_free}"'
    )
    # ----开始自成交操作(在IfSelfTrade==1的时候)----
    if IfSelfTrade == 1:
        # --首先,计算自成交的价格、量等信息--
        global InventoryWant2be
        global BestPrice_Master
        # 随机的交易量,满足条件:在区间(0.11, SelfTradeTotalMax)内
        SelfOrderTotal = 0
        while (SelfOrderTotal < 0.11 or SelfOrderTotal > SelfTradeTotalMax):
            SelfOrderTotal = np.random.exponential(SelfTradeTotalMax / 2)
        # 自成交单子的价格,靠近中间价,谨防被套利
        SelfBuyPrice = ask1_RT - 1 / 2 * np.random.uniform(0.95, 1.05) * (
            ask1_RT - bid1_RT)  # 自成交的买单为当前的中间价附近
        SelfSellPrice = bid1_RT + 1 / 2 * np.random.uniform(0.95, 1.05) * (
            ask1_RT - bid1_RT)  # 自成交的卖单为当前中间价附近
        # --然后,发出自成交的单子,并更新InvestoryWant2be--
        randNum = np.random.uniform(0, 1)  # 随机变量来确定交易量是否取整
        if Direction == 1:
            AccountName = balance1.iloc[0]['index']  # 库存最小的slave执行买入
            SelfOrderType = 'BuyOrder'
            SelfOrderPrice = SelfBuyPrice
            if randNum < 1 / 2:  # 依据一个随机量来确定:(1)下单量取整;(2)不取整
                SelfVolume = SelfOrderTotal / SelfOrderPrice
                if SelfVolume >= 10:
                    SelfVolume = round(SelfVolume, 0) + 1
                else:
                    SelfVolume = round(SelfVolume, 1) + 0.1
            else:
                SelfVolume = SelfOrderTotal / SelfOrderPrice
            SelfOrder1 = await create_order(account_manager, 'Master', 'Sell',
                                            SelfVolume, SelfOrderPrice, 5)
            SelfOrder2 = await create_order(account_manager, AccountName,
                                            'Buy', SelfVolume, SelfOrderPrice,
                                            1)
            InventoryWant2be = InventoryWant2be - SelfVolume * TakerOrderCost  # 山寨币买方为吃单,用Taker费率计算交易费用,再更新合宜库存量
        elif Direction == -1:
            AccountName = balance1.iloc[-1]['index']  # 库存最大的slave执行卖出
            SelfOrderType = 'SellOrder'
            SelfOrderPrice = SelfSellPrice
            if randNum < 1 / 2:  # 依据一个随机量来确定:(1)下单量取整;(2)不取整
                SelfVolume = round(SelfOrderTotal / SelfOrderPrice, 0) + 1
            else:
                SelfVolume = SelfOrderTotal / SelfOrderPrice
            SelfOrder1 = await create_order(account_manager, 'Master', 'Buy',
                                            SelfVolume, SelfOrderPrice, 5)
            SelfOrder2 = await create_order(account_manager, AccountName,
                                            'Sell', SelfVolume, SelfOrderPrice,
                                            1)
            InventoryWant2be = InventoryWant2be - SelfVolume * MakerOrderCost  # 山寨币买方为挂单,用Maker费率计算交易费用,再更新合宜库存量
        logger.info(
            f'"data": "--SelfTradeOccurs: {AccountName} send a {SelfOrderType} at {SelfOrderPrice} with (amount,total):({SelfVolume},{SelfOrderTotal}), Update InventoryWant2be({InventoryWant2be})--"'
        )
Example #8
0
async def send_order(account_manager, balance, orderbook):
    """
    根据计算的orderbook发送山寨币订单的函数:
    send_order(account_manager,balance,orderbook)
    依据计算的orderbook向dex发送订单簿
    (**)优化方向:如何自适应orderbook的档位(比如,想挂15个档位)?
    
    Input:
    account_manager:账户管理的类
    balance:账户信息的dataframe
    orderbook:之前计算好的想要达到的orderbook
    """
    # 登录账户
    logger.info('"msg": "send_order start"')
    try:
        await account_manager.login()
    except:
        logger.error('"error": "Account Login Timeout!"')
        raise
    # ----判断Master账户能不能提交orderbook----
    balance_Master = balance.reset_index()
    balance_Master = balance_Master[balance_Master['index'] == 'Master']
    ETH_all_Master = balance_Master['cash_total'][0]  # Master的账户中ETH总额
    Inventory_all_Master = balance_Master['Inventory_total'][
        0]  # Master的账户中山寨币总额
    orderbook_Master = orderbook.copy()
    orderbook_Master[
        'bid_cash'] = orderbook_Master['bid'] * orderbook_Master['bidS']
    bid_all_Master = orderbook_Master['bid_cash'].sum()  # Master要挂出的所有买单之和
    ask_all_Master = orderbook_Master['askS'].sum()  # Master要挂出的所有卖单之和
    if (bid_all_Master >= ETH_all_Master) or (ask_all_Master >=
                                              Inventory_all_Master):
        logger.error(
            f"error: Master Account have No Enough ETH or Inventory! Please Check!"
        )
    # ----开始执行订单操作----
    # --先撤销Master的所有订单--
    NowTimeStamp = f'{int(time.time()*1000)}'
    Master_cancel_all = await account_manager.Master.cancel_all_orders(
        symbol, NowTimeStamp)
    # --再依据orderbook下单--
    # bid1和ask1先下单下去,其余的orderbook部分一起下
    logger.info('"msg": "place order"')
    global ISOrderbookUpdateNotOK  # 如果没有挂单成功,那么下一轮循环继续挂单
    ISOrderbookUpdateNotOK = 0
    try:
        try:
            bid1 = await create_order(account_manager, 'Master', 'Buy',
                                      orderbook['bidS'][0],
                                      orderbook['bid'][0])
        except Exception as e:
            logger.error(
                f'"error": "place order bid1 {bid1} error :[{type(e)}]  {e}"')
            raise
        else:
            logger.info(f'"msg": "place order bid1 {bid1}"')
        try:
            ask1 = await create_order(account_manager, 'Master', 'Sell',
                                      orderbook['askS'][0],
                                      orderbook['ask'][0])
        except Exception as e:
            logger.error(
                f'"error": "place order ask1 {ask1} error :[{type(e)}]  {e}"')
            raise
        else:
            logger.info(f'"msg": "place order ask1 {ask1}"')
        bid2 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][1], orderbook['bid'][1])
        logger.info(
            f""""msg": "place order bid2: orderId is {bid2['order']['orderId']}" """
        )
        ask2 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][1], orderbook['ask'][1])
        logger.info(
            f""""msg": "place order ask2: orderId is {ask2['order']['orderId']}" """
        )
        bid3 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][2], orderbook['bid'][2])
        logger.info(
            f""""msg": "place order bid3: orderId is {bid3['order']['orderId']}" """
        )
        ask3 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][2], orderbook['ask'][2])
        logger.info(
            f""""msg": "place order ask3: orderId is {ask3['order']['orderId']}" """
        )
        bid4 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][3], orderbook['bid'][3])
        logger.info(
            f""""msg": "place order bid4: orderId is {bid4['order']['orderId']}" """
        )
        ask4 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][3], orderbook['ask'][3])
        logger.info(
            f""""msg": "place order ask4: orderId is {ask4['order']['orderId']}" """
        )
        bid5 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][4], orderbook['bid'][4])
        logger.info(
            f""""msg": "place order bid5: orderId is {bid5['order']['orderId']}" """
        )
        ask5 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][4], orderbook['ask'][4])
        logger.info(
            f""""msg": "place order ask5: orderId is {ask5['order']['orderId']}" """
        )
        bid6 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][5], orderbook['bid'][5])
        logger.info(
            f""""msg": "place order bid6: orderId is {bid6['order']['orderId']}" """
        )
        ask6 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][5], orderbook['ask'][5])
        logger.info(
            f""""msg": "place order ask6: orderId is {ask6['order']['orderId']}" """
        )
        bid7 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][6], orderbook['bid'][6])
        logger.info(
            f""""msg": "place order bid7: orderId is {bid7['order']['orderId']}" """
        )
        ask7 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][6], orderbook['ask'][6])
        logger.info(
            f""""msg": "place order ask7: orderId is {ask7['order']['orderId']}" """
        )
        bid8 = await create_order(account_manager, 'Master', 'Buy',
                                  orderbook['bidS'][7], orderbook['bid'][7])
        logger.info(
            f""""msg": "place order bid8: orderId is {bid8['order']['orderId']}" """
        )
        ask8 = await create_order(account_manager, 'Master', 'Sell',
                                  orderbook['askS'][7], orderbook['ask'][7])
        logger.info(
            f""""msg": "place order ask8: orderId is {ask8['order']['orderId']}" """
        )
    except Exception as e:
        ISOrderbookUpdateNotOK = 1
        logger.info('"msg": "--Warning: Orderbook Update Not Scucess"')
    # --记录一下Master自己挂出来的bid1和ask1,更新到一个缓存数据结构BestPrice_Master--
    global BestPrice_Master
    ask1_Master = round(orderbook['ask'][0], 8)
    bid1_Master = round(orderbook['bid'][0], 8)
    BestPrice_Master.append([ask1_Master, bid1_Master])
    midP_Master = round((ask1_Master + bid1_Master) / 2, 8)
    logger.info(
        f'"data": "Master place ask1({ask1_Master}) and bid1({bid1_Master}), midP_Master is {midP_Master}"'
    )
    # --最后更新一下orderbookUpdateCount--
    # (注意)这里放在最后,是因为必须有了orderbook下单才算真的做完了一轮
    global orderbookUpdateCount
    orderbookUpdateCount = orderbookUpdateCount + 5  # 一旦orderbook出现了Update,则加快自成交(让orderbookUpdateCount增加)
Example #9
0
def calcul_orderbook(Fairvalue, spread, OBdf):
    """
    计算orderbook的函数:
    最重要的是控制ask1和bid1的价格,由Fairvalue和spread给出:
    ask1=Fairvalue+1/2*spread
    bid1=Fairvalue-1/2*spread
    最优档的挂单量askS1和bidS1,暂时先由给定的最优档挂单金额(BestPriceOrderNum)计算得到(会加入一点随机量)
    二档以上的价格、挂单量,由随机量给出
    (**)如果中间价波动很大,则spread需要进行放宽处理
    
    orderbook = calcul_orderbook(Fairvalue,spread)
    Input:
    Fairvalue:公平的中间价
    spread:最优买卖价差
    OBdf:处理后的历史orderbook数据(5分钟采样)
    Output:
    orderbook:一个dataframe,有10档价格,4个columns分别为卖单价格(ask1)、卖单量(askS1)、买单价格(bid1)、买单量(bidS1)
    """
    # ----信息预处理----
    ask1_b_10min = OBdf['ask1'][len(OBdf) - 2]
    bid1_b_10min = OBdf['bid1'][len(OBdf) - 2]
    midP_orderbook = Fairvalue
    midP_b_10min = (ask1_b_10min + bid1_b_10min) / 2
    Diff_OB_RT = abs(midP_orderbook / midP_b_10min -
                     1)  # 更新的orderbook的中间价和10min前的中间价的偏差(%)
    spreadRatio = spread / Fairvalue  # 更新的orderbook的spread比例(spread/midPrice)(%)
    spreadRatio_b_10min = 2 * (ask1_b_10min - bid1_b_10min) / (ask1_b_10min +
                                                               bid1_b_10min)
    # --计算spread放宽系数--
    # 如果波动偏差大于一个spread比例,我们就认为需要放宽spread(如,spreadRaio=5%,则波动偏差超过5%时,我们要放宽spread来自我保护)
    # 注意:如果10分钟前的(ask1,bid1)数据计算的spreadRatio_b_10min超过了100%,就认为那个时点的挂单出现了错误,则不进行spread放宽调整
    # 随后的orderbook更新,如果没有需要放宽调整的情况,则每次都降低一点,直至1
    global spreadWidenCoef
    if Diff_OB_RT >= spreadRatio and spreadRatio_b_10min <= 100 * 1e-2:
        x = Diff_OB_RT / spreadRatio  # x = 波动偏差/spread比例
        spreadWidenCoef = max(x, spreadWidenCoef *
                              0.9)  # 需要调整时,spread放宽系数:x和(0.9*系数前值)中的最大的那一个
    else:
        spreadWidenCoef = spreadWidenCoef * 0.9  # 不需要调整时,spread放宽系数:(0.9*系数前值),随着orderbook的更新递减,直至为1
        if spreadWidenCoef < 1:
            spreadWidenCoef = 1
    logger.info(
        f'"data": "Orderbook Update with spreadWidenCoef({spreadWidenCoef}) -- Diff_OB_RT({Diff_OB_RT}) / spreadRatio{spreadRatio}"'
    )
    # ----ask1、bid1信息----
    # --ask1、bid1价格(依据波动情况进行调整)--
    spread_Adjust = spread * spreadWidenCoef
    ask1 = Fairvalue + 1 / 2 * spread_Adjust
    bid1 = Fairvalue - 1 / 2 * spread_Adjust
    # --ask1、bid1挂单量:服从指数分布,但是数量必须满足一定条件(超出范围重新设值)--
    askS1, bidS1 = 0, 0
    while (askS1 < 0.1 or askS1 > 3 * BestPriceOrderNum or bidS1 < 0.1
           or bidS1 > 3 * BestPriceOrderNum):
        askS1 = np.random.exponential(BestPriceOrderNum)
        bidS1 = np.random.exponential(BestPriceOrderNum)
    # ----askn、bidn信息(n>=1)----
    spreadRatio_0_5 = 0.5 * (spread / Fairvalue)
    spreadRatio_1_5 = 1.5 * (spread / Fairvalue)
    delta_ask = list(
        np.log(np.random.uniform(1 + spreadRatio_0_5, 1 + spreadRatio_1_5, 9)))
    delta_bid = list(
        np.log(np.random.uniform(1 - spreadRatio_0_5, 1 - spreadRatio_1_5, 9)))
    delta_ask, delta_bid = [np.log(ask1)] + delta_ask, [np.log(bid1)
                                                        ] + delta_bid  # 合并
    askS = list(np.random.uniform(0.1, nonBestPriceOrderNumMax, 9))
    bidS = list(np.random.uniform(0.1, nonBestPriceOrderNumMax, 9))
    askS, bidS = [askS1] + askS, [bidS1] + bidS  # 合并
    # --生成一个orderbook的DataFrame--
    orderbook = pd.DataFrame(
        {
            'delta_ask': delta_ask,
            'askS_ETH': askS,
            'delta_bid': delta_bid,
            'bidS_ETH': bidS
        },
        index=range(10))
    orderbook['ask'] = np.e**(orderbook['delta_ask'].cumsum())
    orderbook['askS'] = orderbook['askS_ETH'] / orderbook['ask']
    orderbook['bid'] = np.e**(orderbook['delta_bid'].cumsum())
    orderbook['bidS'] = orderbook['bidS_ETH'] / orderbook['bid']
    orderbook = orderbook[['ask', 'askS', 'bid', 'bidS']]
    return orderbook
Example #10
0
def data_transform_His(cache):
    """
    处理历史数据,benchmark和dex的值都可能要转换
    
    OBdf = data_transform_His(cache)
    Input:
    cache:缓存的过去一段时间的历史数据
    Ouput:
    OBdf:一个dataframe,根据条件对benchmark和dex的历史价格转换,然后获得的一个ask1、bid1、ask1_BM、bid1_BM四列数的表
    """
    # --取数据--
    OBdf = cache.to_dataframe()
    # --历史orderbook数据不够的报警(可能是数据库的bug)--
    if len(OBdf) < ((TimeWindow * 12) * 0.5):
        logger.error(
            f'"error": "Hisdata OBdf has {len(OBdf)} rows, TimeWindow should have {TimeWindow*12} rows, Please Check DataBase!"'
        )
    # --benchmark数据--
    # 以ETH/USDT作为benchmark,需要将ETH/USDT转换为一个模拟价格:(1-b)*山寨币初始价格+(山寨币初始价格*b/ETH初始价格)*ETH价格
    ETH2BMPrice = b * Price0 / ETH0  # 转换系数
    if IfUsingETHasBM == 1:
        OBdf['ask1_BM'] = (1 - b) * Price0 + ETH2BMPrice * OBdf['ask1_BM']
        OBdf['bid1_BM'] = (1 - b) * Price0 + ETH2BMPrice * OBdf['bid1_BM']
    # --dex数据--
    # 不用历史数据时,需要构造一个虚拟的山寨币历史数据,使得历史数据满足spread0、PDiff0
    if IfNotUsingHisData == 1:
        OBdf['midP0'] = (OBdf['ask1_BM'] + OBdf['bid1_BM']
                         ) / 2 + PDiff0  # 模拟的中间价:benchmark的中间价加上给定价差PDiff0
        OBdf['spread0'] = OBdf[
            'midP0'] * spreadRatio0  # 模拟的spread0:中间价乘以给定的价差比例spreadRatio0
        OBdf['ask1_rand'] = np.random.uniform(
            -1 / 2, 1 / 2, len(OBdf))  # 计算ask1和bid1,加上了一点随机量
        OBdf['bid1_rand'] = np.random.uniform(-1 / 2, 1 / 2, len(OBdf))
        OBdf['ask1'] = OBdf['midP0'] + (1 / 2 +
                                        OBdf['ask1_rand']) * OBdf['spread0']
        OBdf['bid1'] = OBdf['midP0'] - (1 / 2 +
                                        OBdf['bid1_rand']) * OBdf['spread0']
        OBdf = OBdf[['ask1', 'bid1', 'ask1_BM', 'bid1_BM']]
    # --剔除明显错误数据(3个标准差以外的值删除)--
    OBdf_mean = OBdf.mean()
    OBdf_std = OBdf.std()
    bid1_sup, bid1_inf = OBdf_mean['bid1'] + 3 * OBdf_std['bid1'], OBdf_mean[
        'bid1'] - 3 * OBdf_std['bid1']
    ask1_sup, ask1_inf = OBdf_mean['ask1'] + 3 * OBdf_std['ask1'], OBdf_mean[
        'ask1'] - 3 * OBdf_std['ask1']
    bid1_BM_sup, bid1_BM_inf = OBdf_mean['bid1_BM'] + 3 * OBdf_std[
        'bid1_BM'], OBdf_mean['bid1_BM'] - 3 * OBdf_std['bid1_BM']
    ask1_BM_sup, ask1_BM_inf = OBdf_mean['ask1_BM'] + 3 * OBdf_std[
        'ask1_BM'], OBdf_mean['ask1_BM'] - 3 * OBdf_std['ask1_BM']
    OBdf = OBdf[(bid1_inf < OBdf['bid1']) & (OBdf['bid1'] < bid1_sup) &
                (ask1_inf < OBdf['ask1']) &
                (OBdf['ask1'] < ask1_sup)]  # 自己交易所的筛选
    OBdf = OBdf[(bid1_BM_inf < OBdf['bid1_BM']) &
                (OBdf['bid1_BM'] < bid1_BM_sup) &
                (ask1_BM_inf < OBdf['ask1_BM']) &
                (OBdf['ask1_BM'] < ask1_BM_sup)]  # benchmark交易所的筛选

    logger.info(
        f'"data": "Get Hisdata OBdf, the last row is: {OBdf.iloc[-1]}"')

    return OBdf
Example #11
0
def db_sql():
    logger.info('"msg": "db init!"')
    # ----从数据库读取数据----
    # --sql读数--
    dt_begin = datetime.datetime.now() - (TimeWindow +
                                          8) * Hour()  # datetime是0时区,now()是东8区
    dt_begin_str = dt_begin.strftime("%Y-%m-%d %H:%M:%S")
    # 数据库连接
    engine = create_engine(db_url)
    if IfNotUsingHisData == 0:
        symbol_sql = symbol
    else:
        symbol_sql = symbol_HisData
    sql = f"""
      SELECT *
      FROM
      (
      SELECT trade_market,symbol,datetime,date,CAST(asks->0->>0 as NUMERIC) as ask1,CAST(bids->0->>0 as NUMERIC) as bid1
      FROM orderbook
      WHERE ((symbol='{symbol_sql}' and trade_market='{MMmarket}') or (symbol='{symbol_BM}' and trade_market='{benchmark}'))
      and datetime>='{dt_begin_str}'
      ) as a
      WHERE mod(extract(minute from datetime)::int,5)=0
      """
    df = pd.read_sql(sql, engine)
    logger.info('"msg": "db init get data!"')
    # --预处理:时间对齐--
    # 首先对值进行lead、lag处理
    df1 = df.drop(['symbol', 'date'], axis=1).sort_values('datetime')
    df1 = df1.dropna(axis=0, how='any')  # 去掉包含了NAN的行
    df1_lag1, df1_lead1 = df1.shift(1), df1.shift(-1)
    df1_lag1, df1_lead1 = df1_lag1.rename(
        columns=lambda x: x + '_lag1'), df1_lead1.rename(
            columns=lambda x: x + '_lead1')
    df1 = pd.concat([df1, df1_lag1, df1_lead1], axis=1).dropna(axis=0,
                                                               how='any')

    # 每一行判断lead、lag哪个更近,用更近的ask1和bid1作为benchmark(尽可能对齐)
    def neardt(arrLike):
        trade_market, datetime, datetime_lag1, datetime_lead1 = arrLike[[
            'trade_market', 'datetime', 'datetime_lag1', 'datetime_lead1'
        ]]
        ask1_lag1, bid1_lag1, ask1_lead1, bid1_lead1 = arrLike[[
            'ask1_lag1', 'bid1_lag1', 'ask1_lead1', 'bid1_lead1'
        ]]
        trade_market_lag1, trade_market_lead1 = arrLike[[
            'trade_market_lag1', 'trade_market_lead1'
        ]]
        if trade_market == MMmarket:
            if trade_market_lag1 == benchmark and trade_market_lead1 == benchmark:  # 前后都是benchmark,比较时间远近
                if abs(datetime - datetime_lag1) <= abs(datetime -
                                                        datetime_lead1):
                    ask1, bid1 = ask1_lag1, bid1_lag1
                else:
                    ask1, bid1 = ask1_lead1, bid1_lead1
            elif trade_market_lag1 != benchmark and trade_market_lead1 == benchmark:  # lag1的不是benchmark,则直接用lead1赋值
                ask1, bid1 = ask1_lead1, bid1_lead1
            elif trade_market_lag1 == benchmark and trade_market_lead1 != benchmark:  # lead1的不是benchmark,则直接用lag1赋值
                ask1, bid1 = ask1_lag1, bid1_lag1
            else:  # lag1和lead1都不是benchmark,则不赋值
                ask1, bid1 = np.NaN, np.NaN
        else:
            ask1, bid1 = np.NaN, np.NaN
        return ask1, bid1

    df1['BM'] = df1.apply(neardt, axis=1)
    df1['ask1_BM'] = df1['BM'].apply(lambda x: x[0])
    df1['bid1_BM'] = df1['BM'].apply(lambda x: x[1])
    # 只看dex且前后至少有一个benchmark的观测,保留下需要的数据
    OBdf = df1[(df1['trade_market'] == MMmarket)
               & ((df1['trade_market_lag1'] != MMmarket)
                  | (df1['trade_market_lead1'] != MMmarket))]
    OBdf = OBdf[['datetime', 'ask1', 'bid1', 'ask1_BM', 'bid1_BM']]
    OBdf = OBdf.reset_index().drop(columns='index')
    OBdf = OBdf.set_index('datetime')
    global cache
    cache = Cache(OBdf.values.tolist(),
                  columns=['ask1', 'bid1', 'ask1_BM', 'bid1_BM'])
    logger.info('"msg": "db init cache data!"')
Example #12
0
async def main():
    # 定义loop
    loop = asyncio.get_event_loop()
    # 登录账户
    logger.info('"msg": "init account"')
    all_account = [Account(clz=dex, **i) for i in ACCOUNTS
                   ]  # 注意,这里的登录方法用的是MMmarket名,需要检查是否一致(目前dex和dex_test是一致的)
    logger.info('"msg": "init account manager"')
    account_manager = AccountManager(all_account)
    # 起一个协程来接收benchmark的数据
    BM_ch = CurrentData("depth", benchmark, timeout=3, symbol=symbol_BM)
    BM2_ch = CurrentData("depth", benchmark2, timeout=3, symbol=symbol_BM)
    # benchmark频道的错误计数
    benchmarkDataCount = 0
    # 由推送数据驱动
    logger.info('"msg": "init sub"')
    async with Subscribe("depth", MMmarket, symbol) as MM_ch:
        logger.info(
            '"msg": "#################################This Round Start#################################"'
        )
        async for msg in MM_ch:
            try:
                logger.info(f'"data": "Latest raw data {cache._content[-1]}"')
                # 如果benchmark的timeout错误连续出现了benmarkDataCountMax次,则转而读取备用benchmark频道
                if benchmarkDataCount < benmarkDataCountMax:
                    logger.info(
                        f'"msg": "Try benchmark channel: {benchmarkDataCount+1} times"'
                    )
                    msg_BM = BM_ch.value
                    benchmarkDataCount = 0  # 从benchmark频道中成功读数就重置为0
                else:
                    logger.error(
                        f'"error": "There is someting wrong with benchmark channel, Try benchmark2 channel: {benchmarkDataCount-3} times, Please Check Channel ({benchmark})!"'
                    )
                    msg_BM = BM2_ch.value
                    benchmarkDataCount = 0  # 从benchmark备用频道中成功读数就重置为0
            except Exception as e:
                benchmarkDataCount = benchmarkDataCount + 1
                print(benchmarkDataCount)
                logger.info(f'"error": "Get Benchmark Data error {str(e)}"')
                continue

            asyncio.ensure_future(
                deal_with_data(msg, msg_BM, BestPrice_Master, cache,
                               account_manager, loop))