import asyncio

import pandas as pd

from traderion2.bot import TraderionBot, BID, ASK
from traderion2.client import TraderionClient

BEARISH_CROSS, BULLISH_CROSS = (0, 1)

DEBUG = False


class EmaBot(TraderionBot):
    """
    The bot implements a simple crossing moving average strategy using
    the 5 period EMA as the signal and the 20 period EMA.

    When a crossing happens the bot fills its position to the position limit
    in the respective direction and waits for a signal in the other direction.

    When this happens it unloads its positions and loads in the new direction
    using the same position limit as target.

    In case we get the same values for the two EMAs from the initial price curve,
    the bot will sell/buy when the signal drops below/rises above the base EMA.
    """

    def __init__(self, username, password, room_id, loop_sleep=1):
        super().__init__(username, password, room_id, loop_sleep)

        self.swift = self.api.get_swifts()[0]
        self.swift_id = self.swift['swift_id']

        # Session attributes
        self.position = self.api.get_positions(self.swift_id)
        self.depth = self.api.get_eb_depth(self.swift_id)
        self.price_curve = [o['price'] for o in self.api.get_price_curves(self.swift_id)]
        self.room_params = self.api.get_room_params()
        self.max_ticket = self.room_params[str(self.swift_id)]['PLAYERS_MAX_TICKET']
        self.position_limit = self.room_params[str(self.swift_id)]['PLAYERS_POSITION_LIMIT']

        # Strategy attributes
        self.ema_5 = None
        self.ema_20 = None
        self.compute_emas()
        self.last_cross = None

    async def on_market_price_change(self, old_price, new_price):
        self.price_curve = [o['price'] for o in self.api.get_price_curves(self.swift_id)]
        self.compute_emas()

    async def on_position_change(self, old_position, new_position):
        if new_position['swift_id'] == self.swift_id:
            self.position = new_position

    async def on_eb_depth_change(self, swift_id, direction, old_depth, new_depth):
        if swift_id == self.swift_id:
            self.depth[direction] = new_depth
        # print('typeof new_depth', type(new_depth))

    async def main_loop(self):
        await self.run_strategy()

    def compute_emas(self):
        self.ema_5 = round(pd.Series(self.price_curve).ewm(span=5).mean().tolist()[-1],
                           self.swift['decimal_places'])
        self.ema_20 = round(pd.Series(self.price_curve).ewm(span=20).mean().tolist()[-1],
                            self.swift['decimal_places'])

    async def run_strategy(self):
        cross_direction = self.get_new_cross_direction()

        if cross_direction is not None:
            self.last_cross = cross_direction
            await self.load(cross_direction)

    def get_new_cross_direction(self):
        cross_direction = None
        if self.last_cross in [BEARISH_CROSS, None] and self.ema_5 > self.ema_20:
            cross_direction = BULLISH_CROSS
        elif self.last_cross in [BULLISH_CROSS, None] and self.ema_5 < self.ema_20:
            cross_direction = BEARISH_CROSS
        return cross_direction

    async def load(self, direction):
        current_amount = self.position['amount'] / self.swift['ticket_unit']  # short amount
        target_amount = self.position_limit   # short amount, absolute
        sgn = (-1, 1)[direction == BULLISH_CROSS]
        remaining_amount = target_amount - sgn * current_amount
        if remaining_amount < 0:
            return
        else:
            while remaining_amount > 0 and direction == self.last_cross:
                ticket = self.max_ticket \
                    if remaining_amount >= self.max_ticket else remaining_amount
                amount = (await self.api.hit_price(self.swift_id, direction, ticket, self.depth[direction][0]['price']))['data']
                remaining_amount -= amount

    async def log(self):
        if not DEBUG:
            return

        while True:
            print('Position amount@rate:', self.position['amount'], '@', self.position['rate'])
            print('Pnl:', self.position['pnl'])
            print('Last cross:', self.last_cross)
            print('5 EMA:', self.ema_5)
            print('20 EMA:', self.ema_20)
            await asyncio.sleep(1)


def main():
    username = 'YOUR_USERNAME'
    password = 'YOUR_PASSWORD'
    room_id = 'ROOM_ID'

    bot = EmaBot(username, password, room_id)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.gather(bot.run(), bot.log()))


if __name__ == '__main__':
    main()