import sys, os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import my_config as config

import logging

import pandas as pd
import numpy as np

import json

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__file__)


def getCumReturn(close, start, n):
    sum = 0
    #Maxim hold days
    n += 1

    for index in xrange(1, n, 1):
        tomorrow = start + index
        today = start + index - 1
        sum += (close[tomorrow] / close[today] - 1)

    return sum/(n-1)

#daily_rate_array, buy day is not counted in the range
def calReturnArray(ac_np, index, days):
    daily_rate = []
    cur_close = ac_np[index]
    # 0 to 34

    daily_rate.append(0)
    #normalized
    for i in xrange(1, days, 1):
        daily_rate.append((ac_np[index + i] / cur_close - 1.0)/i)
    return daily_rate

def calReturnStatics(return_array):
    bull_return_rate = -100
    bull_hold_days = 0
    bear_return_rate = 100
    bear_hold_days = 0

    for index, return_value in enumerate(return_array):
        if bull_return_rate < return_value:
            bull_return_rate = return_value
            bull_hold_days = index
        if bear_return_rate > return_value:
            bear_return_rate = return_value
            bear_hold_days = index
    #Convert the bear's return rate from negative to positive.
    return bull_return_rate, bull_hold_days, -bear_return_rate, bear_hold_days

#bull_return_rate, bull_hold_days
def getExtremReturn(ac_np, index, n):
    np_len = len(ac_np)
    n = min(np_len - index, n)
    return_array = calReturnArray(ac_np, index, n)

    # Convert the bear's return rate from negative to positive.
    bull_return_rate, bull_hold_days, bear_return_rate, bear_hold_days = calReturnStatics(return_array)
    return bull_return_rate, bull_hold_days, bear_return_rate, bear_hold_days

'''
Given an index and sticker, get the best return from 5 to n days
'''
def getBestReturn(ac_np, index, n):
    hold_days = -1
    best_return_rate = 0  # CASH is the best if nothing else

    cur_aclose = ac_np[index]
    length = len(ac_np)

    if index + config.MIN_HOLD_DAYS < length:
        start_day = config.MIN_HOLD_DAYS
        end_day = n
    else:
        start_day = 1
        end_day = config.MIN_HOLD_DAYS

    for days in xrange(start_day, end_day + 1, 1):
        if index + days < length:
            return_rate = float(ac_np[index + days]) / float(cur_aclose) - 1.0

            if return_rate > best_return_rate:
                hold_days = days
                best_return_rate = return_rate

    return best_return_rate, hold_days

#Calculate the highest perc and lowest perc
#This function returns
#If long:
def calHighLowRange(close, high, low, index, hold_days, tag):
    cur_close = close[index]
    enter_close = cur_close
    prev_close = cur_close
    highest = -1
    lowest = 100000
    low_rate = 1000
    short_low_rate = -1000
    #including current day
    for i in xrange(1, hold_days + 1, 1):
        cur_high = high[index + i]
        cur_low  = low[index + i]
        cur_close = close[index + i]

        if cur_high > highest:
            highest = cur_high

        if cur_low < lowest:
            lowest = cur_low

        if tag == 'bull':
            daily_return_rate = cur_close / prev_close - 1.0
            if daily_return_rate < low_rate:
                low_rate = daily_return_rate
        if tag == 'bear':
            daily_return_rate = 1.0 - cur_close/prev_close
            if daily_return_rate < low_rate:
                low_rate = daily_return_rate

        prev_close = cur_close

    if tag == 'bull':
        high_rate = highest/enter_close - 1.0

    if tag == 'bear':
        high_rate = enter_close/lowest - 1.0

    return high_rate, low_rate

# _bull_sticker, _bull_return, _bull_hold_days, _bull_lowest_perc, _bull_highest_perc, \
# _bear_sticker, _bear_return, _bear_hold_days,_bear_lowest_perc, _bear_highest_perc
# Calculate and get the extreme best and worst choice.
def getExtremeChoice(stickers, df, index, n):

    best_bull_return_rate = -1
    best_bear_return_rate = -1

    bull_hold_days = 0
    bear_hold_days = 0

    bull_sticker = 'CASH'
    bear_sticker = 'CASH'

    bull_lowest_perc = 0
    bull_highest_perc = 0

    bear_lowest_perc = 0
    bear_highest_perc = 0

    for sticker in stickers:
        ac_name = sticker + "_close"

        ac_np = np.asarray(df[ac_name])
        high_np = np.asarray(df[sticker + "_high"])
        low_np = np.asarray(df[sticker + "_low"])

        #Get 0 - 35 days' best bull/bear return_rate and holding days
        #The buy-in day is the 0th day
        #If hold days = 2, it means buy on 0th, hold 1st and sell on 2nd day
        #If hold days =6, it means buy on 0th, hold 1,2,3,4,5 and sell on 6th day
        bull_return_rate, _bull_hold_days, bear_return_rate, _bear_hold_days = getExtremReturn(ac_np, index, n)

        if _bull_hold_days >= config.MIN_HOLD_DAYS:
            #Change here, is >1.05
            if bull_return_rate > best_bull_return_rate and bull_return_rate > 0:
                best_bull_return_rate = bull_return_rate
                bull_hold_days = _bull_hold_days
                bull_sticker = sticker

                #Long opt, highest perc and lowest perc
                bull_highest_perc, bull_lowest_perc = calHighLowRange(ac_np, high_np, low_np, index, bull_hold_days, 'bull')
        if _bear_hold_days >= config.MIN_HOLD_DAYS:
            #Change here, is >1.05
            if bear_return_rate > best_bear_return_rate and bear_return_rate > 0:
                best_bear_return_rate = bear_return_rate
                bear_hold_days = _bear_hold_days
                bear_sticker = sticker
                #Short opt, highest return perc and lowest perc
                #This rate has been converted
                bear_highest_perc, bear_lowest_perc = calHighLowRange(ac_np, high_np, low_np, index, bear_hold_days, 'bear')

    if bull_sticker == 'CASH':
        bull_return_rate = 0

    if bear_sticker == 'CASH':
        bear_return_rate = 0

    return bull_sticker, best_bull_return_rate, bull_hold_days, bull_lowest_perc, bull_highest_perc, \
           bear_sticker, best_bear_return_rate, bear_hold_days, bear_lowest_perc, bear_highest_perc

'''
At every index date, calculate each sticker's MIN_HOLD_DAYS to n days' average return
Get the sticker and hold days n
'''

def getBestChoice(df, index, n):
    _best_return_rate = -1
    _hold_days = n          #If all stickers have negative return, by default hold 'CASH' for n days
                            #so here _hold_days is befault to be n(25) days
    _best_choice = 'CASH'
    for sticker in config.STICKERS:
        ac_name = sticker + "_close"
        ac_np = np.asarray(df[ac_name])

        best_return_rate, hold_days = getBestReturn(ac_np, index, n)
        if hold_days >= config.MIN_HOLD_DAYS:
            #Change here, is >1.05
            if best_return_rate > _best_return_rate and best_return_rate >= 0.01:
                _best_return_rate = best_return_rate
                _hold_days = hold_days
                _best_choice = sticker
    _lowest_perc = 0
    _highest_perc = 0#getVolatility(df, index, _best_choice, _hold_days)
    return _best_return_rate, _hold_days, _best_choice, _lowest_perc, _highest_perc


# best_trade_records, worst_trade_records
# Calculate best and worst trade records
def calExtremeReturn(stickers, df, start_index, end_index, n):

    # Main a buy quant list and a sell quant list
    long_record = []
    short_record = []

    #For each sticker, calculate its 5-n days ROC
    index = start_index

    while (index < end_index ):
        if index + config.MIN_HOLD_DAYS >= end_index:
            break

        _bull_sticker, _bull_return, _bull_hold_days, _bull_lowest_perc, _bull_highest_perc, \
        _bear_sticker, _bear_return, _bear_hold_days,_bear_lowest_perc, _bear_highest_perc = getExtremeChoice(stickers, df, index, n)

        if _bull_sticker != 'CASH':
            # Constrain the _hold_days ...
            if index + _bull_hold_days >= end_index:
                _bull_hold_days = end_index - index
            return_rate = _bull_return * _bull_hold_days
            if _bull_lowest_perc <0 and return_rate >= 0.02:
                long_record.append({'index':index,
                                'sticker':_bull_sticker,
                                'hold_days':_bull_hold_days,
                                'return_rate':return_rate,
                                'lowest_perc':_bull_lowest_perc,
                                'highest_perc':_bull_highest_perc})
            print "Long Complete..." + str(float(index)/float(end_index) * 100.0) + '%...'

        if _bear_sticker != 'CASH':
            if index + _bear_hold_days >= end_index:
                _bear_hold_days = end_index - index
            return_rate = _bear_return * _bear_hold_days
            if _bear_lowest_perc < 0 and _bear_return * _bear_hold_days >= 0.02:
                short_record.append({'index':index,
                                'sticker':_bear_sticker,
                                'hold_days':_bear_hold_days,
                                'return_rate':return_rate,
                                'lowest_perc':_bear_lowest_perc,
                                'highest_perc':_bear_highest_perc})
            print "Short Complete..." + str(float(index)/float(end_index) * 100.0) + '%...'

        #Best trade every mininum trading days...
        index += config.MIN_HOLD_DAYS
    return long_record, short_record

def parseRecord(record):
    index = record['index']
    hold_days = int(record['hold_days'])
    return_rate = record['return_rate']
    sticker = record['sticker']
    low_per = record['lowest_perc']
    high_per = record['highest_perc']

    return index, hold_days, return_rate, sticker, low_per, high_per

def fetchCodePerf(quant_json, hold_quant):
    return quant_json[hold_quant]['high_perc'], quant_json[hold_quant]['low_perc']


def fetchQuantCode(quant_df, index, sticker):
    #print quant_df.columns.values
    d_q = quant_df.iloc[index][sticker + '_daily']
    w_q = quant_df.iloc[index][sticker + '_weekly']
    f_q = quant_df.iloc[index]['FUND_PERF']
    return d_q + w_q + f_q

def getTradeQuantDict(quant_df, best_trade_records):

    trade_quant_dict = {}
    size = quant_df.shape[0]

    counter = 0
    for record in best_trade_records:
        counter += 1
        index, hold_days, return_rate, sticker, low_perc, high_perc = parseRecord(record)

        #Get the buy_quant_code from quant_df
        buy_quant_code = fetchQuantCode(quant_df, index, sticker)

        if buy_quant_code not in trade_quant_dict:
            trade_quant_dict[buy_quant_code] = {}
            trade_quant_dict[buy_quant_code]['sell_quant'] = []
            trade_quant_dict[buy_quant_code]['hold_quant'] = []
            trade_quant_dict[buy_quant_code]['low_perc'] = []
            trade_quant_dict[buy_quant_code]['high_perc'] = []

        end_index = index + hold_days
        if end_index >= size:
            end_index = size -1
        for day in xrange(index, end_index, 1):
            hold_quant_code = fetchQuantCode(quant_df, day, sticker)
            if hold_quant_code not in trade_quant_dict[buy_quant_code]['hold_quant']:
                trade_quant_dict[buy_quant_code]['hold_quant'].append(hold_quant_code)
        sell_quant_code = fetchQuantCode(quant_df, end_index, sticker)
        trade_quant_dict[buy_quant_code]['sell_quant'].append(sell_quant_code)
        trade_quant_dict[buy_quant_code]['low_perc'].append(low_perc)
        trade_quant_dict[buy_quant_code]['high_perc'].append(high_perc)

    return trade_quant_dict



