# This file is part of the iaif_utils package
#  mt5se home: https://github.com/paulo-al-castro/iaif_utils
# Author: Paulo Al Castro
# Date: 2026-03-02

##########################################

import numpy as np
import pandas as pd


# X is an array of arrays with values 
def ts2Dataset(X,timeFrame):
    ds=np.array()
    for line in range(len(X)-timeFrame):
        for x in X:
            for i in range(timeFrame):
                ds=ds.append(x[line+i])

    n_fields=len(X)
    size=len(ds)
    ds.reshape(size/(n_fields*timeFrame),n_fields*timeFrame)
    return ds
# From bars to dataset
def bars2Dataset(bars,target,timeFrame,horizon=1):
	ds=pd.DataFrame()
	lines=len(bars)
	for time in range(timeFrame):
		for s in bars.keys():
			aux=bars[s][time:lines-horizon-timeFrame+time]
			aux=aux.reset_index(drop=True)
			#del aux['index']
			#print('aux type=',type(aux))
			ds[s+str(time)]=aux
	#print(bars[target][timeFrame+horizon:].shift(-timeFrame),' timeF=',timeFrame,' h=',horizon  )
	aux=bars[target][timeFrame+horizon-1:]
	aux=aux.reset_index(drop=True)
	#del aux['index']
	ds['target']=aux
	return ds

def fromDs2NpArrayAllBut(ds,fieldList):
	all=ds.keys()
	for f in fieldList:
		all=all.drop(f)
	fieldList=[]
	for f in all:
		fieldList.append(f)
	return fromDs2NpArray(ds,fieldList)

# se.ai_utils.get_X( dataframe,features list, time frame ) -> returns a np array
# From bars to dataset
def get_X(df,attr_list,timeFrame,horizon):
	ds=pd.DataFrame()
	lines=len(df)
	for time in range(timeFrame):
		for s in df.keys():
			if s in attr_list:
				aux=df[s][time:lines-timeFrame-horizon+time]
				aux=aux.reset_index(drop=True)
				#del aux['index']
				#print('aux type=',type(aux))
				ds[s+str(time)]=aux
	#print(bars[target][timeFrame+horizon:].shift(-timeFrame),' timeF=',timeFrame,' h=',horizon  )
	#aux=aux.reset_index(drop=True)
	#del aux['index']
	return np.array(ds)


# se.ai_utils.get_X( dataframe,features list, time frame ) -> returns a np array
# From bars to dataset
def get_Y(df,target,timeFrame,horizon):
	Y=pd.DataFrame()
	lines=len(df)
	aux=df[target][timeFrame+horizon:]
	aux=aux.reset_index(drop=True)
	#del aux['index']
	Y['target']=aux

	return np.array(Y)

def get_XY(df,feature_list,target,timeFrame,horizon):
	return get_X(df,feature_list,timeFrame,horizon),get_Y(df,target,timeFrame,horizon)

def fromDs2NpArray(ds,fieldList=[]):
	nfields=len(fieldList)
	#print('nfields=',nfields)
	if nfields==0:
		return None
	elif nfields==1:
		return np.array(ds[fieldList[0]])
	else:
		a=np.array(ds[fieldList[0]])
	
	for i in range(1,nfields):
		a=np.column_stack((a,np.array(ds[fieldList[i]])))
	return a



def discTarget(discretizer,target):
	x=np.array(target)
	x=x.reshape(-1,1)
	dx=discretizer.fit_transform(x) 
	return dx


def get_close_prices_from_dbars(assets,dbars,option=1):
    """
Returns a pd.DataFrame like the one below for a group of assets from given
 multi asset bars
   The price can be the close price (default) or the average of open,close,high,low
                XOM        RRC        BBY         MA        PFE        JPM
date
2010-01-04  54.068794  51.300568  32.524055  22.062426  13.940202  35.175220
2010-01-05  54.279907  51.993038  33.349487  21.997149  13.741367  35.856571
2010-01-06  54.749043  51.690697  33.090542  22.081820  13.697187  36.053574
2010-01-07  54.577045  51.593170  33.616547  21.937523  13.645634  36.767757
2010-01-08  54.358093  52.597733  32.297466  21.945297  13.756095  36.677460
 Note that 'date' column is the index, the others are assets' close prices
"""
    df=pd.DataFrame()
    first=True
    for asset in assets:
        bars=dbars[asset]
        if first:
            df['date']=bars['time']
            first=False
        if option==1:
            df[asset]=bars['close']
        elif option==2:
            df[asset]=bars['open']/4+bars['close']/4+bars['high']/4+bars['low']/4
    df=df.set_index('date')
    return df


def mean_historical_return(df,geometric=True):
    """
    Returns the historical mean, it can be geometric (default) or arithmetic
    If the data is daily, the mean is daily.
"""
    expected_returns=dict()
    assets=df.keys()
    for asset in assets:
        size=len(df[asset])
        ret=0
        if not geometric: #arithmetic
            for i in range(size-1):
                ret=ret+(df[asset][i+1]/df[asset][i]-1)
            expected_returns[asset]=ret/(size-1)
        else:
            ret=(df[asset][size-1]/df[asset][0])
            expected_returns[asset]=ret**(1/(size-1))-1         
    return expected_returns



def get_last_prices(assets,dbars=None):
    """
    Returns a dictionary with the last prices of set of assets
        the symbol tickets are the dictionary keys
"""
    last_prices=dict()
    if dbars is not None:
        for asset in assets:
            last_prices[asset]=get_last(dbars[asset])
    else:
        return None
    return last_prices



def get_returns(bars):
    """
  Returns a serie of returns from given bars using open-close prices
    close[i]/open[i]-1
"""

    x=[]
    for i in range(len(bars)):
        x.append(bars['close'][i]/bars['open'][i]-1)
    return x


def get_last(bars): # argumento deve ser bars nao vazia, retorna erro se estiver vazia
    """
  Returns the last close price from given bars
"""
    if bars is None:
        return 0.0
    return bars['close'].iloc[-1]


def get_first(bars):# argumento deve ser bars nao vazia, retorna erro se estiver vazia
    """
  Returns the first open price from given bars
"""
    if bars is None:
        return 0
    return bars['open'][0]

def get_max(bars):# argumento deve ser bars nao vazia, retorna erro se estiver vazia
    """
  Returns the max high price from given bars
"""
    if bars is None:
        return 0
    return max(bars['high'])


def get_min(bars):# argumento deve ser bars nao vazia, retorna erro se estiver vazia
    """
  Returns the min low price from given bars
"""
    if bars is None:
        return 0
    return max(bars['low'])


def get_last_time(bars): # argumento deve ser bars nao vazia, retorna erro se estiver vazia
    """
  Returns the last time from given bars
"""
    if bars is None:
        return 0
    return bars['time'].iloc[-1]

def get_first_time(bars):# argumento deve ser bars nao vazia, retorna erro se estiver vazia
    """
  Returns the first (earlier) time from given bars
"""
    if bars is None:
        return 0
    return bars['time'][0]




def y(y,timeFrame):
    return y[-timeFrame:]



