import pandas as pd
from concurrent.futures import ProcessPoolExecutor
from functools import partial
from scipy.optimize import minimize
from core.utility import draw_sample, sortino

""" Find the best weights of instruments in a portfolio """

def bootstrap(portfolio, n=1500, costs=True, **kw):
    data = portfolio.curve(portfolio_weights=1,capital=10E7).returns()
    sample_length = 300
    samples = [draw_sample(data, length=sample_length).index for k in range(0,n)]
    weights = list(mp_optimize_weights(samples, data, **kw))
    weights_buffer = pd.DataFrame([x for x in weights if type(x) == pd.Series])
    print(len(weights_buffer),"samples")
    weights_buffer.mean().plot.bar()
    return weights_buffer

def mp_optimize_weights(samples, data, **kw):
    return ProcessPoolExecutor().map(partial(optimize_weights, data), samples)

def optimize_weights(data, sample):
    data = data.loc[sample].dropna(axis=1, how='all')
    if data.shape[1] < 2:
        return
    guess = [1] * data.shape[1]
    bounds = [(0.0, 10.0)] * data.shape[1]

    def function(w, data, sample):
        wr = (w*data.loc[sample]).sum(axis=1)
        return -sortino(wr)

    result = minimize(function, guess, (data, sample),\
                      method = 'SLSQP',\
                      bounds = bounds,\
                      tol = 0.0001,\
                      constraints = {'type': 'eq', 'fun': lambda x:  data.shape[1] - sum(x)},\
                      options = {'eps': 1e-1},
                      )
    return pd.Series(result.x, index=data.columns)