У меня есть два кадра данных: the_strongest_stock
и data
.Они имеют одинаковый мультииндекс: Date
и Symbol
.the_strongest_stock
имеет один соответствующий столбец: Entry_signal
.data
имеет столбцы Open
, High
, Low
, Close
, ATR
, No_of_data
.Я присоединяю the_strongest_stock
к data
и регистрирую позиции.
# Entry signal---------------------------------------------------------------
data = data.join(the_strongest_stock[['Entry_signal']], on=['Date','Symbol'])
data.loc[data['No_of_data'] < 250,'Entry_signal'] = False
data['Entry_signal'] = data['Entry_signal'].fillna(False)
# Position log---------------------------------------------------------------
data['Initial_stop'] = (data['Close'] - 10 * data['ATR']).round(2)
data['Stop_level'] = data.groupby(['Symbol',data.groupby('Symbol')['Entry_signal'].cumsum()])['Initial_stop'].cummax()
data['Exit_signal'] = np.where(data['Low'] < data.groupby('Symbol')['Stop_level'].shift(1),True,False)
data['Position'] = np.where(data['Exit_signal'] == True,False,np.where(data['Entry_signal'] == True,True,np.nan))
data['Position'] = data.groupby('Symbol')['Position'].ffill()
data.loc[data['Position'] == False,'Stop_level'] = False
Теперь, когда у меня есть агрегированный фрейм данных data
с зарегистрированными Position
с, я создаю столбцы для расчета плавающей прибыли и закрытияприбыль, которую сгенерировали сделки.
# Shares---------------------------------------------------------------
starting_capital = 30000
risk_base = starting_capital
trade_base = starting_capital
close_stop_diff = np.maximum(data.groupby('Symbol')['Close'].shift(1) - data.groupby('Symbol')['Stop_level'].shift(1),0.000001) # to avoid division by zero in share_number
share_number = (risk_base * risk_per_position) / close_stop_diff
data['Shares'] = np.where(
data['Position'] == False,
0,
np.where(
np.logical_and(
data.groupby('Symbol')['Position'].shift(1) == False,
data['Position'] == True),
np.where(
share_number * data.groupby('Symbol')['Close'].shift(1) > trade_base + 0.05 * risk_base,
0,
share_number),
np.nan))
data = data.sort_values(by=['Date', 'Symbol'])
data['Shares'] = data.groupby('Symbol')['Shares'].ffill()
data['Shares'] = data['Shares'].astype(float).round(0)
# Open price---------------------------------------------------------------
data.loc[data['Position'] == False, 'Open_price'] = False
data['Open_price'] = np.where(
data['Exit_signal'] == True,
False,
np.where(
np.logical_and(
data.groupby('Symbol')['Position'].shift(1) == False,
data['Position'] == True),
data['Open'] + skid * (data['High'] - data['Open']),
np.nan))
data = data.sort_values(by=['Date', 'Symbol'])
data['Open_price'] = data.groupby('Symbol')['Open_price'].ffill()
data['Open_price'] = data['Open_price'].round(2)
# Open value---------------------------------------------------------------
data['Open_value'] = np.where(
data['Position'] == True,
data['Shares'] * data['Open_price'],
0)
# Floating profit---------------------------------------------------------------
data['Floating_P/L'] = np.where(
data['Position'] == True,
data['Shares'] * (data['Close'] - data['Open_price']),
0)
data['Floating_P/L'] = data['Floating_P/L'].fillna(0)
data['Floating_P/L'] = data['Floating_P/L'].round(2)
# Close price---------------------------------------------------------------
data['Close_price'] = np.where(
np.logical_and(
data.groupby('Symbol')['Position'].shift(1) == True,
data['Position'] == False),
data.groupby('Symbol')['Stop_level'].shift(1) - skid * (data.groupby('Symbol')['Stop_level'].shift(1) - data['Low']),
False)
data['Close_price'] = data['Close_price'].astype(float).round(2)
# Closed profit---------------------------------------------------------------
data['Closed_P/L'] = np.where(
np.logical_and(
data.groupby('Symbol')['Position'].shift(1) == True,
data['Position'] == False),
data.groupby('Symbol')['Shares'].shift(1) * (data['Close_price'] - data.groupby('Symbol')['Open_price'].shift(1)),
0)
data['Closed_P/L'] = data['Closed_P/L'].fillna(0)
data['Closed_P/L'] = data['Closed_P/L'].round(2)
Наконец, я создаю столбцы, чтобы суммировать прибыль и убыток для каждого Date
, генерируемого Symbol
s
# Totals---------------------------------------------------------------
data['Total_Open_Value'] = data.groupby(by=['Date','Symbol'])['Open_value'].sum().groupby(level=[0]).cumsum().groupby('Date').tail(1)
data['Total_Open_Value'] = data.groupby('Date')['Total_Open_Value'].bfill()
data['Floating_Total'] = data.groupby(by=['Date','Symbol'])['Floating_P/L'].sum().groupby(level=[0]).cumsum().groupby('Date').tail(1)
data['Floating_Total'] = data.groupby('Date')['Floating_Total'].bfill()
data['Closed_Total'] = data.groupby(by=['Date','Symbol'])['Closed_P/L'].sum().groupby(level=[0]).cumsum().groupby('Date').tail(1).cumsum()
data['Closed_Total'] = data.groupby('Date')['Closed_Total'].bfill()
data['Closed_Balance'] = starting_capital + data['Closed_Total'] - data['Total_Open_Value']
data['Equity'] = data['Closed_Balance'] + data['Floating_Total'] + data['Total_Open_Value']
Моя проблемачто таким образом starting_capital
является постоянным числом (средняя секция кода, первая строка кода), и я хочу вычислить значение столбца Share
для данного Date
на основе предыдущего Date
* Equity
и Closed_Balance
.Я попытался переназначить новое содержимое для соответствующих переменных, которые используются для вычисления Shares
, в конце кода следующим образом:
risk_base = data.groupby('Date')['Equity'].tail(1).shift(1)
trade_base = data.groupby('Date')['Closed_Balance'].tail(1).shift(1)
, но это не приводит к динамическому изменению столбца Shares
ценности.Мне бы хотелось, чтобы столбец Shares
знал, что из второго индекса Date
он должен рассчитываться с предыдущими значениями Date
Equity
и Closed_Balance
.По первому индексу Date
он должен вычисляться с переменной starting_capital
, которая является начальным значением для Equity
и Closed_Balance
.Как я могу сделать эту работу?Я знаю, что Equity
/ Closed_Balance
рассчитывается на основе Shares
, а Shares
рассчитывается на основе Equity
/ Closed_Balance
, но последний должен сдвинуть на один Date
индекс «вверх» в Equity
/ Closed_Balance
, и они также имеют начальные значения.Так что логика должна быть в порядке, не так ли?Я застрял в этом вопросе на несколько дней, было бы здорово, если бы кто-то мог помочь.Спасибо!