Пользовательская функция для создания индекса результатов - PullRequest
0 голосов
/ 19 января 2011

Я пытаюсь создать функцию, которая создает индекс (начиная с 100), а затем корректирует этот индекс в соответствии с результатами инвестиций.Итак, в двух словах: если первая инвестиция дает прибыль 5%, то индекс будет равен 105, если второй результат равен -7%, то индекс будет 97,65.В этом вопросе, когда я использую слово «индекс», я не , ссылаясь на функцию index пакета zoo.

Помимо создания этого индекса, мой цель - также создать функцию, которая может быть применена к различным подмножествам моего полного набора данных (т. е. с использованием sapply и его друзей).

Вот функция, которая у меня есть такfar (данные в конце этого вопроса):

CalculateIndex <- function(x){
    totalAccount <- accountValueStart
    if(x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1)){
        indexedValues <- 100 + ( 100 *((((x$Size.Units. * x$EntryPrice) / totalAccount) * x$TradeResult.Percent.) / 100))
        # Update the accountvalue
        totalAccount <- totalAccount + x$TradeResult.Currency.
    }   
    else{ # the value is not the first
        indexedValues <- c(indexedValues, 
                indexedValues[-1] + (indexedValues[-1] *(((x$Size.Units. * x$EntryPrice) / totalAccount) * x$TradeResult.Percent.) / 100)
                )
            # Update the accountvalue
            totalAccount <- totalAccount + x$TradeResult.Currency.      
    }
    return(indexedValues)
}

В словах функция выполняет (читай: предназначена сделать) следующее: Если значение является первым, используйте 100 какотправная точка для индекса.Если значение не является первым, используйте предыдущее вычисленное значение индекса в качестве отправной точки для расчета нового значения индекса. Кроме того, функция также учитывает вес отдельного результата (по сравнению со значением totalAccount)

Проблема: Использование этой функции CalculateIndex во фрейме данных theData дает следующий неверный вывод:

> CalculateIndex(theData)
 [1]  99.97901  99.94180  99.65632 101.88689 100.89309  98.92878 102.02911 100.49159  98.52955 102.02243  98.43655 100.76502  99.34869 100.76401 101.18014  99.75136  97.90130
[18] 100.39935  99.81311 101.34961
Warning message:
In if (x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1)) { :
  the condition has length > 1 and only the first element will be used

Редактировать: Ух ты, я уже получил голосование, хотя думал, что мой вопрос уже слишком длинный.Извините, я думал / думаю, что проблема заключалась в моем цикле, поэтому я не хотел утомлять вас деталями, которые, как я думал, дадут меньше ответов.Извините, неправильное суждение с моей стороны.

Проблема заключается в том, что при вышеприведенном выводе CalculateIndex результаты сильно отличаются от Excel.Даже если это может быть результатом ошибок округления (как упоминает Джорис ниже), я сомневаюсь в этом.По сравнению с результатами Excel результаты R довольно сильно отличаются:

R output    Excel calculate values  
99,9790085700   99,97900857 
99,9418035700   99,92081189 
99,6563228600   99,57713687 
101,8868850000  101,4639947 
100,8930864300  102,3570786 
98,9287771400   101,2858564 
102,0291071400  103,3149664 
100,4915864300  103,806556  
98,5295542900   102,3361186 
102,0224285700  104,3585552 
98,4365550000   102,795089  
100,7650171400  103,5601228 
99,3486857100   102,9087897 
100,7640057100  103,6728077 
101,1801400000  104,8529634 
99,7513600000   104,6043164 
97,9013000000   102,5055298 
100,3993485700  102,9048999 
99,8131085700   102,7179995 
101,3496071400  104,0676555 

Я думаю, было бы справедливо сказать, что разница в результатах не является результатом проблем R и Excel, но скорееошибка в моей функции.Итак, давайте сосредоточимся на функции.

Ручное вычисление функции Функция использует различные переменные:

  • Size.Units.;это количество единиц, которые покупаются по EntryPrice.
  • EntryPrice: цене, по которой покупаются акции,
  • TradeResult.Percent.: процентная прибыль или убыток, полученный в результатеот инвестиции,
  • TradeResult.Currency.: валютная стоимость ($) прибыли или убытка от инвестиций,

Эти переменные используются в следующем разделе функции:

100 + ( 100 *((((x$Size.Units. * x$EntryPrice) / totalAccount) * x$TradeResult.Percent.) / 100))

и

indexedValues[-1] + (indexedValues[-1] *(((x$Size.Units. * x$EntryPrice) / totalAccount) * x$TradeResult.Percent.) / 100)

Обе формулы по сути одинаковы, с той разницей, что первая начинается с 100, а вторая использует previous valueрассчитать новое индексированное значение.

Формула может быть разбита на несколько этапов:

Во-первых, x$Size.Units. * x$EntryPrice определяет общую позицию , которая была занята, в том смысле, что покупка 100 акций поцена 48,98 дает позицию 4898 долл.

Итоговая общая позиция затем делится на общий размер счета (т. е. totalAccount).Это необходимо для корректировки влияния одной позиции по отношению ко всему портфелю.Например, если наши 100 акций, купленные по 48,98, упали на 10 процентов, вычисленный индекс (то есть функция CalculateIndex) не должен упасть на 10%, потому что, конечно, не все деньги в totalAccount инвестируется в одну акцию.Итак, поделив общую позицию на totalAccount, мы получим коэффициент, который говорит нам, сколько денег вложено.Например, позиция размером 4898 долларов США (на общем счете 14000) приводит к общей потере счета в 3,49%, если акция падает на 10%.(т.е. 4898 / 14000 = 0.349857. 0.349857 * 10% = 3.49857%)

Это соотношение (суммы инвестирования к общей сумме) затем в формуле умножается на x$TradeResult.Percent., чтобы получить процентное влияние на общую сумму (см. Пример расчета в предыдущем абзаце).

В качестве последнего шага процентная потеря на счете total применяется к значению индекса (которое начинается с 100). В этом случае первая инвестиция в 100 акций, купленная за 48,89 доллара, позволила индексу упасть с начальной точки на 100 до 99,97901, отражая влияние убыточной сделки на общий счет.

Конец редактирования

Очистка функции и последующее добавление части формулы за раз, чтобы выявить ошибку, я пришел к следующему шагу, где ошибка, по-видимому, находится:

CalculateIndex <- function(x){
    totalAccount <- accountValueStart
    if(x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1)){
        indexedValues <- totalAccount
        # Update the accountvalue
        totalAccount <- totalAccount + x$TradeResult.Currency.
    }   
    else{ # the value is not the first
        indexedValues <- c(indexedValues, totalAccount)         
            # Update the accountvalue
            totalAccount <- totalAccount + x$TradeResult.Currency.      
    }
    return(indexedValues)
}
> CalculateIndex(theData)
[1] 14000
Warning message:
In if (x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1)) { :
  the condition has length > 1 and only the first element will be used

Итак, похоже, что если я просто использую переменную totalAccount, функция не будет обновлена ​​правильно. Это говорит о том, что в основах оператора if else есть некоторая ошибка, поскольку он выводит только первое значение.

Если я удалю оператор else из функции, я получу значения для каждой строки в theData. Тем не менее, они тогда неправильно рассчитаны. Итак, мне кажется, что в этой функции обновляется переменная totalAccount, что-то не так. Я не вижу, где я сделал ошибку, поэтому любые предложения будут высоко оценены. Что я делаю не так?


Данные

Вот как выглядят мои данные:

> theData
   Size.Units. EntryPrice TradeResult.Percent. TradeResult.Currency.
1          100      48.98                -0.06                    -3
11         100      32.59                -0.25                    -8
12         100      32.51                -1.48                   -48
2          100      49.01                 5.39                   264
13         100      32.99                 3.79                   125
14         100      34.24                -4.38                  -150
3          100      51.65                 5.50                   284
4          100      48.81                 1.41                    69
15         100      35.74                -5.76                  -206
5          100      49.50                 5.72                   283
6          100      46.67                -4.69                  -219
16         100      33.68                 3.18                   107
7          100      44.48                -2.05                   -91
17         100      32.61                 3.28                   107
8          100      45.39                 3.64                   165
9          100      47.04                -0.74                   -35
10         100      47.39                -6.20                  -294
18         100      33.68                 1.66                    56
19         100      33.12                -0.79                   -26
20         100      32.86                 5.75                   189

theData <- structure(list(X = c(1L, 11L, 12L, 2L, 13L, 14L, 3L, 4L, 15L, 
    5L, 6L, 16L, 7L, 17L, 8L, 9L, 10L, 18L, 19L, 20L), Size.Units. = c(100L, 
    100L, 100L, 100L, 100L, 100L, 100L, 100L, 100L, 100L, 100L, 100L, 
    100L, 100L, 100L, 100L, 100L, 100L, 100L, 100L), EntryPrice = c(48.98, 
    32.59, 32.51, 49.01, 32.99, 34.24, 51.65, 48.81, 35.74, 49.5, 
    46.67, 33.68, 44.48, 32.61, 45.39, 47.04, 47.39, 33.68, 33.12, 
    32.86), TradeResult.Percent. = c(-0.06, -0.25, -1.48, 5.39, 3.79, 
    -4.38, 5.5, 1.41, -5.76, 5.72, -4.69, 3.18, -2.05, 3.28, 3.64, 
    -0.74, -6.2, 1.66, -0.79, 5.75), TradeResult.Currency. = c(-3L, 
    -8L, -48L, 264L, 125L, -150L, 284L, 69L, -206L, 283L, -219L, 
    107L, -91L, 107L, 165L, -35L, -294L, 56L, -26L, 189L)), .Names = c("X", 
    "Size.Units.", "EntryPrice", "TradeResult.Percent.", "TradeResult.Currency."
    ), class = "data.frame", row.names = c(NA, -20L))

# Set the account start @ 14000
> accountValueStart <- 14000

Ответы [ 3 ]

8 голосов
/ 19 января 2011

Ваш код выглядит очень странно, и, похоже, у вас есть много заблуждений относительно R, пришедших из другого языка программирования. Гэвин и Гиллеспи уже указали, почему вы получаете предупреждение. Позвольте мне добавить несколько советов по более оптимальному кодированию:

  • [- 1] НЕ означает: отбросить последний. Это означает «сохранить все, кроме первого значения», что также объясняет, почему вы получаете ошибочные результаты.

  • вычислите общие вещи в начале, чтобы загромождать ваш код.

  • head(x$TradeResult.Currency., n = 1) совпадает с x$TradeResult.Currency.[1].

  • Следите за вашими векторами. Большинство ошибок в вашем коде происходят из-за того, что вы забыли, что работаете с векторами.

  • Если вам нужно, чтобы значение было первым в векторе, поместите это ВНЕ любого цикла, который вы используете, никогда не добавляйте в функцию выражение if.

  • предопределяет ваши векторы / матрицы как можно больше, это идет намного быстрее и дает меньше головной боли при работе с большими данными.

  • векторизация, векторизация, векторизация . Я упоминал векторизация ?

  • Узнайте, как использовать debug(), debugonce() и browser(), чтобы проверить, что делает ваша функция. Многие из ваших проблем могли быть решены путем проверки объектов при манипуляциях внутри функции.

Это сказанное и учтенное, ваша функция становится:

CalculateIndex <- function(x,accountValueStart){
  # predifine your vector
  indexedValues <- vector("numeric",nrow(x))
  # get your totalAccount calculated FAST. This is a VECTOR!!!
  totalAccount <- cumsum(c(accountValueStart,x$TradeResult.Currency.))
  #adjust length:
  totalAccount <- totalAccount[-(nrow(x)+1)]

  # only once this calculation. This is a VECTOR!!!!
  totRatio <- 1+(((x$Size.Units. * x$EntryPrice)/totalAccount) *
                 x$TradeResult.Percent.)/100

  # and now the calculations
  indexedValues[1] <- 100 * totRatio[1]
  for(i in 2:nrow(x)){
      indexedValues[i] <- indexedValues[i-1]*totRatio[i]
  }
  return(indexedValues)
}

и возвращает

> CalculateIndex(theData,14000)
[1]  99.97901  99.92081  99.57714 101.46399 102.35708 101.28586 103.31497 
 103.80656 102.33612 104.35856 102.79509 103.56012
[13] 102.90879 103.67281 104.85296 104.60432 102.50553 102.90490 102.71800 
 104.06766

Итак, теперь вы делаете:

 invisible(replicate(10,print("I will never forget about vectorization any more!")))
4 голосов
/ 19 января 2011

С этой строки приходит предупреждающее сообщение:

if(x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1)){

Легко понять, почему;x$TradeResult.Currency является вектором и, следовательно, сравнение с head(x$TradeResult.Currency., n = 1) дает вектор логических элементов.(Кстати, почему бы не x$TradeResult.Currency[1] вместо вызова head()?).if() требует одного логического, а не вектора логических элементов, и это то, о чем говорится в предупреждении.ifelse() полезно, если вы хотите выполнить одну из двух вещей в зависимости от условия, которое дает вектор логики.

По сути, вы делаете только ввод части if() оператора ион выполняется только один раз, потому что первый элемент x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1) равен TRUE, а R. игнорирует остальные.

> if(c(TRUE, FALSE)) {
+ print("Hi")
+ } else {
+ print("Bye")
+ }
[1] "Hi"
Warning message:
In if (c(TRUE, FALSE)) { :
  the condition has length > 1 and only the first element will be used
> ifelse(c(TRUE, FALSE), print("Hi"), print("Bye"))
[1] "Hi"
[1] "Bye"
[1] "Hi"  "Bye"

Что касается решения вашей реальной проблемы:

CalculateIndex2 <- function(x, value, start = 100) {
    rowSeq <- seq_len(NROW(x))
    totalAc <- cumsum(c(value, x$TradeResult.Currency.))[rowSeq]
    idx <- numeric(length = nrow(x))
    interm <- (((x$Size.Units. * x$EntryPrice) / totalAc) *
               x$TradeResult.Percent.) / 100
    for(i in rowSeq) {
        idx[i] <- start + (start * interm[i])
        start <- idx[i]
    }
    idx
}

, которыйпри использовании на theData дает:

> CalculateIndex2(theData, 14000)
 [1]  99.97901  99.92081  99.57714 101.46399 102.35708 101.28586 103.31497
 [8] 103.80656 102.33612 104.35856 102.79509 103.56012 102.90879 103.67281
[15] 104.85296 104.60432 102.50553 102.90490 102.71800 104.06766

Что вы хотите - это рекурсивная функция (IIRC);текущий индекс является некоторой функцией предыдущего индекса.Это трудно решить векторизованным способом в R, следовательно, цикл.

2 голосов
/ 19 января 2011

Я все еще немного сбит с толку относительно того, что именно вы хотите сделать, но, надеюсь, вам поможет следующее.

Ваш скрипт R дает те же ответы, что и ваша функция Excel, для первого значения. Вы видите разницу, потому что R не печатает все цифры.

> tmp = CalculateIndex(thedata)
Warning message:
In if (x$TradeResult.Currency == head(x$TradeResult.Currency., n = 1)) { :
  the condition has length > 1 and only the first element will be used
> print(tmp, digits=10)
 [1]  99.97900857  99.94180357  99.65632286 101.88688500 100.89308643
 <snip>

Причина появления предупреждающего сообщения в том, что x$TradeResult.Currency - это вектор, который сравнивается с одним числом.

Это предупреждающее сообщение также там, где живет ваша ошибка. В вашем операторе if вы никогда не выполняете остальную часть, поскольку используется только значение x$TradeResult.Currency. Как говорится в предупреждающем сообщении, используется только первый элемент x$TradeResult.Currency.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...