Блог им. Riskplayer
Best params (4, 8)Т.е. система следующая: если SMA(4) пересекает снизу вверх SMA(8), то покупаем, если пересекает сверху вниз, то ликвидируем позицию.
SBER average profit = 1.654% pf = 2.3 Max win = 31.144% max loss -18.70% GAZP average profit = 0.883% pf = 1.7 Max win = 37.161% max loss -7.26% GMKN average profit = 0.728% pf = 1.6 Max win = 19.071% max loss -14.99% LKOH average profit = 0.365% pf = 1.2 Max win = 19.565% max loss -9.32% ROSN average profit = 0.920% pf = 1.8 Max win = 20.785% max loss -13.21% MOEX average profit = 0.989% pf = 1.8 Max win = 29.939% max loss -8.98% NVTK average profit = 0.591% pf = 1.4 Max win = 25.483% max loss -11.18% PLZL average profit = 2.084% pf = 2.4 Max win = 64.463% max loss -20.14% AFLT average profit = 1.125% pf = 1.7 Max win = 37.095% max loss -9.32% MTLR average profit = 3.068% pf = 2.8 Max win = 160.692% max loss -25.22% NLMK average profit = 1.057% pf = 1.7 Max win = 34.744% max loss -13.75% MAGN average profit = 1.342% pf = 1.9 Max win = 33.568% max loss -16.13% TRNFP average profit = 0.955% pf = 1.7 Max win = 33.902% max loss -11.36% MGNT average profit = 0.318% pf = 1.2 Max win = 21.875% max loss -18.84%График накопленной доходности без реинвестирования (включая испытательный и контрольный диапазон):
import pandas as pd import numpy as np from datetime import datetime import itertools import talib def get_daily_data(ticker): filename = "..\\moex.h5" with pd.HDFStore(filename) as store: data_1min = store[ticker] data_1min = data_1min[['Close']] #df = data_1min.resample('D').agg({'Open': 'first', 'High': 'max', 'Low': 'min', 'Close': 'last'}) df = data_1min.resample('D').agg({'Close': 'last'}) df = df.dropna() return df def get_arr_of_trades(df, first_timeperiod = 10, second_timeperiod = 30): df['fast_sma'] = talib.SMA(df['Close'], first_timeperiod) df['slow_sma'] = talib.SMA(df['Close'], second_timeperiod) df['signal_buy'] = (df['fast_sma'] > df['slow_sma']) & (df['fast_sma'].shift() < df['slow_sma'].shift()) df['signal_sell'] = (df['fast_sma'] < df['slow_sma']) & (df['fast_sma'].shift() > df['slow_sma'].shift()) df['pos'] = np.NaN df['pos'] = df['pos'].mask(df['signal_buy'], 1) df['pos'] = df['pos'].mask(df['signal_sell'], 0) df.loc[df.index[0], 'pos'] = 0 df['pos'] = df['pos'].ffill() df['diff'] = df['Close'].diff() df['denominator'] = np.NaN df['denominator'] = df['denominator'].mask((df['pos']==1) & (df['pos'].shift() == 0), df['Close']) df['denominator'] = df['denominator'].ffill() df['ret'] = 0 df['ret'] = df['diff']*df['pos'].shift()/df['denominator'] df['cumret'] = df['ret'].cumsum() df['isStartNewTrade'] = 0 df['isStartNewTrade'] = df['isStartNewTrade'].mask((df['pos']==1) & (df['pos'].shift() == 0), 1) df['count'] = df['isStartNewTrade'].cumsum() df['count'] = df['count'].mask(df['pos']== 0, 0) df['count'] = df['count'].shift() df['isFinishTrade'] = False df['isFinishTrade'] = df['isFinishTrade'].mask((df['pos'] == 0) & (df['pos'].shift() == 1), True) df['isFinishTrade'] = df['isFinishTrade'].mask((df['pos'] == 1) & (df.index == df.index[-1]) & (df['pos'].shift() == 1), True) return df list_tickers = ['sber', 'gazp', 'gmkn', 'lkoh', 'rosn', 'moex', 'nvtk', 'plzl', 'aflt', 'mtlr', 'nlmk', 'magn', 'trnfp', 'mgnt'] list_tickers = [x.upper() for x in list_tickers] start_date = datetime(2015, 1, 1) finish_date = datetime(2023, 1, 1) profil = np.zeros((len(list_tickers), 51, 51)) for idx, ticker in enumerate(list_tickers): print(ticker) df = get_daily_data(ticker) df = df[(df.index > start_date) & (df.index < finish_date)] max_period = profil.shape[1] iterator = itertools.combinations(range(2, max_period), 2) for item in iterator: i = item[0] j = item[1] df = get_arr_of_trades(df, i, j) profil[idx, i, j] = df.iloc[-1]['cumret'] sum_profil = profil.mean(axis = 0) result = np.unravel_index(np.argmax(sum_profil), sum_profil.shape) first_timeperiod = result[0] second_timeperiod = result[1] print('Best params', result) rng = pd.date_range(start = start_date.date(), end= datetime.today().date()) rng = rng[rng.weekday < 5] # df_simple для учета простых процентов # df_compound для учета сложного процента df_simple = pd.DataFrame(index = rng) df_compound = pd.DataFrame(index = rng) for idx, ticker in enumerate(list_tickers): df = get_daily_data(ticker) df = df[df.index > start_date] df = get_arr_of_trades(df, first_timeperiod, second_timeperiod) df_simple[ticker] = df['cumret'] trades = df.groupby(['count'])['ret'].sum() trades = trades[trades.index > 0] df_t = pd.DataFrame(data = trades.values, index = df[df['isFinishTrade']].index, columns = [ticker]) df_compound = df_compound.join(df_t) av_profit = trades.mean() pf = - trades[trades > 0].sum()/trades[trades < 0].sum() max_win = trades.max() max_loss = trades.min() print(f'{ticker} average profit = {av_profit:.3%} pf = {pf:.2} Max win = {max_win:.3%} max loss {max_loss:.2%}') df_simple.iloc[0] = df_simple.iloc[0].fillna(0) df_simple = df_simple.ffill() df_simple['average_simple_ret'] = df_simple[list_tickers].mean(axis = 1) #df_simple['average_simple_ret'].plot(grid = True, title = 'Simple percent') df_compound = df_compound.fillna(0) + 1 df_compound = df_compound.cumprod() - 1 df_compound['average_compound_ret'] = df_compound[list_tickers].mean(axis = 1) #df_compound['average_compound_ret'].plot(grid = True, title = 'Compound percent') pd.concat([df_simple['average_simple_ret'], df_compound['average_compound_ret']], axis=1).plot(grid = True)
Работает чуть лучше, чем обычные SMA.
(C — mov(C,S,13)) * V. Под силой Элдер подразумевает объем .
У него сила мишек (L-mov(C,S,13)) * V и сила бычков. Вот с этого (расхождение цены и индекса силы ) и надо начинать. Я начал с этого в 1999г. Закончить ТА можно средней из синусов. Индикаторы ( кроме объема ) есть производное от цены. Их минус = статичный период. Период надо считать через силу тренда. Сила тренда = функция от перекрытия фракталов L-ref(H,-2). Тут фрактал из 1 свечи, те частный случай. Чем тренд сильнее, тем период меньше те 3.Чем тренд слабее, то до 34.