Недавно я написал некоторый код Javascript для создания случайных поддельных данных об акциях, так как хотел показать график, который на первый взгляд выглядел как настоящие данные об акциях, но все, что я придумал, это довольно глупо. Мне просто интересно, есть ли какие-нибудь ресурсы, которые объясняют, как это можно сделать «правильно», то есть, чтобы вы получали реалистично выглядящие данные, которые имеют те же закономерности, что и реальные биржевые данные?
Существуют ли известные методы для создания реалистично выглядящих поддельных данных об акциях?
Ответы (11)
У меня была книга Фрактальный анализ рынка (недавно избавилась от нее), в которой говорилось о статистические свойства цен акций. Не очень полезно для инвестирования, но, возможно, это могло бы помочь вам.
Вам понадобится что-то, что моделирует случайный процесс с желаемыми статистическими свойствами. Двумя примерами случайных процессов являются гауссовский белый шум и винеровский процесс (последний моделирует броуновское движение, а также является пределом случайного блуждания с малыми шагами).
Если я правильно помню из книги «Фрактальный анализ рынка», было утверждение, что логарифм цен акций имеет характеристики, подобные так называемому «шуму 1/f» или "розовый шум", поэтому вы можете поискать статьи о создании розового шума в программном обеспечении. (а затем возьмите результаты и вставьте их в e^x) (редактировать: ой, я неправильно запомнил. Похоже, это больше похоже на дробное броуновское движение)
(Вот хорошее читаемое эссе, в котором рассказывается об истории за изучением фрактальных случайных процессов — и того, как разлив Нила связан с фондовым рынком — к сожалению, это не входит в технические данные, но, возможно, есть такие поисковые термины, как Показатель Херста, с которого можно начать.)
Проблема усложняется, если вам нужно несколько серий биржевых данных. (в этом случае между акциями существует некоторая корреляция, которая зависит от различных общих факторов, например, национальной экономики, типа отрасли и т. д.). Я не уверен, как вы могли бы это сделать, но сначала начните с одного случайного процесса.
Простой алгоритм заключается в использовании простого числа волатильности, которое ограничивает, насколько акции могут измениться в течение заданного периода (скажем, одного дня). Чем выше число, тем выше волатильность. Таким образом, каждый день вы можете вычислять новую цену:
rnd = Random_Float(); // generate number, 0 <= x < 1.0
change_percent = 2 * volatility * rnd;
if (change_percent > volatility)
change_percent -= (2 * volatility);
change_amount = old_price * change_percent;
new_price = old_price + change_amount;
Стабильная акция будет иметь число волатильности, возможно, 2%. Волатильность 10% показала бы довольно большие колебания.
Не идеально, но может выглядеть довольно реалистично.
Образцы
volatility
находится в диапазоне от 1 до 100, то да, вам нужно разделить на 100, как указано в комментарии. Но если ваш volatility
представляет собой число с плавающей запятой от 0 до 1 (т. е. .02
равно 2%), то деление не требуется.
- person Jim Mischel; 02.07.2015
rnd = Random_Float() - 0.5;
, а затем удалите if (change_percent > volatility) change_percent -= (2 * volatility);
- person Lawrence Wagerfield; 22.10.2016
# The following is an adaptation from a program shown at page 140 in
# "Stochastic Simulations and Applications in Finance",
# a book written by Huynh, Lai and Soumaré.
# That program was written in MatLab and this one was written in R by me.
# That program produced many price paths and this one produces one.
# The latter is also somewhat simpler and faster.
# Y is the time period in years, for instance 1 (year)
# NbSteps is the number of steps in the simulation,
# for instance 250 (trading days in a year).
# DeltaY is the resulting time step.
# The computations shown implement the exact solution
# to the stochastic differential equation for
# the geometric Brownian motion modelling stock prices,
# with mean mu and volatility sigma, thus generating a stochastic price path
# such as that exhibited by stock prices when price jumps are rare.
PricePath <- function(Y,NbSteps,mu,sigma,InitPrice) {
DeltaY <- Y/NbSteps; SqrtDeltaY <- sqrt(DeltaY)
DeltaW <- SqrtDeltaY * rnorm(NbSteps)
Increments <- (mu-sigma*sigma/2)*DeltaY + sigma*DeltaW
ExpIncr <- exp(Increments)
PricePath <- cumprod(c(InitPrice,ExpIncr))
return(PricePath)
}
Есть несколько ответов, которые дают вполне хрестоматийный ответ: используйте геометрическое броуновское движение для моделирования цен на акции. Но есть одна важная причина считать это неправильным. Реальные цены акций не ведут себя как геометрическое броуновское движение (GBM). Я объясню это немного позже.
Причина, по которой GBM используется в учебниках для моделирования процесса цен на акции, заключается в простоте. Это поможет вам сдвинуть теорию с мертвой точки и получить некоторые основные результаты, которые кажутся «по существу» правильными. Однако это не означает, что вы должны думать, что именно так «выглядят» цены акций. Это было бы похоже на вывод уравнения движения без учета трения (что теоретически очень полезно), а затем думать, что это то, как движение выглядит в реальной жизни, например. все скользят на своих ботинках, как на коньках.
Одним из теоретически наиболее полезных свойств GBM является то, что будущие изменения не зависят от прошлых изменений. Верно ли это для цен на акции? Неа. Нисколько. Последовательная корреляция встречается повсеместно. Мало того, за большим снижением обычно следует повышенная волатильность, а за большим ростом обычно следует снижение волатильности.
Я предполагаю, что меня могут обвинить в придирках, но эти стилизованные факты широко известны инвесторам и экономистам, поэтому я думаю, будет справедливо сказать, что GBM не выглядит реалистичным для любого, кто знаком с поведением фондового рынка.
Специалисты по эконометрике придумали множество моделей цен на акции. Тот, который, кажется, работает во многих ситуациях, представляет собой авторегрессионную модель для условного среднего в сочетании с моделью типа (G)Arch для волатильности. Для модели волатильности асимметричный GARCH с распределением с толстым хвостом (например, t Стьюдента), по-видимому, лучше всего работает для различных финансовых рынков.
Я написал быструю грязную версию javascript, вдохновленную ответом Питера П. здесь. Мне нужно было создать еженедельные, годовые и общие тренды, чтобы он принимал множество параметров и накладывал их, чтобы получить более сложный (фальшивый) тренд.
function getRandomData(numPoints, center, min, max, cycles)
{
var result = [];
var phase = Math.random() * Math.PI;
var y = center;
function randomPlusMinus() { return (Math.random() * 2) - 1; }
$.each(cycles, function(i,thisCycle) {
thisCycle.phase = Math.random() * Math.PI;
thisCycle.increment = Math.PI / thisCycle.length;
});
for (var i = 0; i < numPoints; i++)
{
$.each(cycles, function(i,thisCycle) {
thisCycle.phase += thisCycle.increment * randomPlusMinus();
y += (Math.sin(thisCycle.phase) * (thisCycle.variance / thisCycle.length) * (randomPlusMinus() * thisCycle.noise)) + (thisCycle.trend / thisCycle.length);
});
if (min) y = Math.max(y,min);
if (max) y = Math.min(y,max);
result.push(y);
}
return result;
}
var data = getRandomData(365,80,20,100,
[{ length: 7, variance: 50, noise: 1, trend: 0},
{ length: 365, variance: 30, noise: 1, trend: 0},
{ length: 700, variance: 2, noise: 0, trend: 100}]);
Я поместил туда диаграмму, чтобы показать результат: http://jsfiddle.net/z64Jr/3/
Я хотел ответить на сообщение Джима Мишеля выше (https://stackoverflow.com/a/8597889/1360592), но так как Я хотел включить код, я вынужден поместить свой ответ здесь.
Основываясь на алгоритме Джима Мишеля, я сделал следующую реализацию Java, и она хорошо работала для моих нужд, генерируя числа, которые при графическом отображении давали визуально привлекательные, реалистично выглядящие котировки акций.
Java:
private float getNextPrice(float oldPrice)
{
// Instead of a fixed volatility, pick a random volatility
// each time, between 2 and 10.
float volatility = _random.nextFloat() * 10 + 2;
float rnd = _random.nextFloat();
float changePercent = 2 * volatility * rnd;
if (changePercent > volatility) {
changePercent -= (2 * volatility);
}
float changeAmount = oldPrice * changePercent/100;
float newPrice = oldPrice + changeAmount;
// Add a ceiling and floor.
if (newPrice < MIN_PRICE) {
newPrice += Math.abs(changeAmount) * 2;
} else if (newPrice > MAX_PRICE) {
newPrice -= Math.abs(changeAmount) * 2;
}
return newPrice;
}
Обратите внимание, что, как указал wiggles в своем комментарии, мне нужно было разделить процент на 100 при объявлении переменной changeAmount.
Взгляните на Yahoo Finance, они предлагают бесплатные отложенные данные с фондовой биржи и графики.
Вот статья об использовании фида: http://www.codeproject.com/KB/aspnet/StockQuote.aspx
Вам понадобится JQuery или вы можете просто использовать XMLHttpRequest для использования службы. К вашему сведению, есть плагин для JQuery для обработки CSV: http://code.google.com/p/js-tables/
Мне нужно было создать фиктивные рыночные данные для симулятора, над которым я работал. Мне нужно было, чтобы данные выглядели как рыночные данные, но оставались в определенных диапазонах, чтобы они были предсказуемы с точки зрения начальной цены, максимума/минимума за день.
В конце концов, я объединил синусоидальные волны разных частот, а затем добавил некоторую случайность, и результаты не просто выглядят хорошо, но и согласуются (вы не получите ничего, что выглядело бы странно). Даже там, где синусоида может быть воспринята, она все еще выглядит нормально.
Случайно сгенерированные рыночные данные
Код написан на языке сценариев BASIC, но он должен быть очень простым для понимания и преобразования на любой нужный вам язык. Получив массив нормализованных данных, умножьте значения на максимальное значение, которое вы хотите получить, чтобы получить ограниченный набор данных.
dim values[] as float
dim offsets[] as integer
dim frequencies[] as float
function GetPoint(x#, f#, a#, o#)
f# = 360.0 / f#
x# = FMod(x# + o#, f#)
angle# = (x# / f#) * 360.0
r# = Sin(angle#) * a#
endfunction r#
function Generate()
// Empty arrays
offsets.Length = -1
frequencies.Length = -1
values.Length = -1
offsets.Insert(Random(0, 359))
offsets.Insert(Random(0, 359))
offsets.Insert(Random(0, 359))
f# = Random(100, 300)
f# = f# / 1000.0
frequencies.Insert(f#)
f# = Random(500, 1000)
f# = f# / 1000.0
frequencies.Insert(f#)
f# = Random(2000, 4000)
f# = f# / 1000.0
frequencies.Insert(f#)
c# = 0
for i = 0 to 1919
v# = 0
v# = v# + GetPoint(i, frequencies[0], 190, offsets[0])
v# = v# + GetPoint(i, frequencies[1], 85, offsets[1])
v# = v# + GetPoint(i, frequencies[2], 40, offsets[2])
r# = Random(0, 40)
r# = r# - 20.0
c# = Clamp(c# + r#, c# - 40, c# + 40)
v# = v# + c#
values.Insert(v#)
next i
start# = values[0]
max# = 0.0
for i = 0 to values.Length
values[i] = values[i] - start#
if Abs(values[i]) > max#
max# = Abs(values[i])
endif
next i
// Normalize
for i = 0 to values.Length
values[i] = (values[i] / max#)
next i
endfunction
function Clamp(v#, min#, max#)
if v# < min#
exitfunction min#
elseif v# > max#
exitfunction max#
endif
endfunction v#
Вот моя попытка в рубине! :) Это выведет строку, которую вы можете скопировать и вставить в диаграммы Google. Я допускаю положительный, отрицательный или нулевой тренд данных. Этот код, вероятно, можно было бы оптимизировать и/или настроить для обеспечения случайности/регулярности.
Диаграммы Google: https://code.google.com/apis/ajax/playground/?type=visualization#line_chart
# In order to generate a semi-realistic looking graph behavior
# we use a sine function to generate period behavior. In order to avoid
# a graph that is too regular, we introduce randomness at two levels:
# The delta between steps across the x-axis is random, but within a range(deltavariance)
# The wavelength of the sine function is varied by randomly incrementing the index we pass
# to the sine function(sine_index)
# CONFIGURATION VARIABLES
yvalue = 1 # start value
range = 100 # y-range
deltavariance = 10 # allowable variance between changes
sine_index, wavelength = 0, 0.33 #index into our sine function that determines whether we change direction or not
i, maxi = 0, 100 # our counter and its maximum
data = {sine_index => yvalue} # seed our data structure with its first value
trend = :positive # :negative, :none # do we want the graph to trend upwards, downwards or neither
periodmin, periodmax = 0, 0 # vars to enforce trending
direction = 1 # start in a positive direction, -1 for negative
# DO NOT EDIT BELOW THIS LINE
while(i < maxi)
olddirection = direction
direction = Math.sin(sine_index).to_f
direction = direction < 0 ? direction.floor : direction.ceil
delta = rand(deltavariance)
yvalue += delta * direction
if trend == :positive
yvalue = periodmin if yvalue < periodmin
periodmin = yvalue if olddirection < direction
elsif trend == :negative
yvalue = periodmax if yvalue > periodmax
periodmax = yvalue if olddirection > direction
end
data[sine_index] = yvalue
sine_index += Math.sin(rand) # Math.sin(rand) will give random numbers from -1..1
i += 1
end
code = <<-CODE
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['x', 'Cats'],
DATASTR
]);
// Create and draw the visualization.
new google.visualization.LineChart(document.getElementById('visualization')).
draw(data, {curveType: "function",
width: 500, height: 400,
vAxis: {maxValue: 10}}
);
}
CODE
datastr = data.collect{|k,v| "[#{k},#{v}]"}.join(",")
code = code.gsub('DATASTR', datastr)
puts code
double price=2000;
while (true) {
double min = (price*-.02);
double max = (price*.02);
double randomNum = ThreadLocalRandom.current().nextDouble(min, max+1);
price=price+randomNum;
System.out.println(price);
}
Это в джаве. Просто нанесите результат в столбец Excel, чтобы увидеть график. Используйте большой набор значений для построения графика в Excel. Интересно видеть, насколько это похоже на реальные биржевые данные.
Вот код, который я создал для своего использования. Цены создаются для новой свечи, которая включает в себя открытие, максимум, минимум, закрытие и объем. Новые цены генерируются на основе % волатильности. Я использовал всего 5% для цен.
Код основан на C#.
public class PriceBar
{
public DateTime Date { get; set; }
public double Open { get; set; }
public double High { get; set; }
public double Low { get; set; }
public double Close { get; set; }
public long Volume { get; set; }
}
public static double GetRandomNumber(double minimum, double maximum)
{
Random random = new Random();
return random.NextDouble() * (maximum - minimum) + minimum;
}
public static void GenerateRandomBar(PriceBar newBar)
{
double fluct = 0.025;
double volFluct = 0.40;
//Open is equal to the previous close
newBar.Open = newBar.Close;
newBar.Close = GetRandomNumber(newBar.Close - newBar.Close * fluct, newBar.Close + newBar.Close * fluct);
newBar.High = GetRandomNumber(Math.Max(newBar.Close, newBar.Open), Math.Max(newBar.Close, newBar.Open) + Math.Abs(newBar.Close - newBar.Open) * fluct);
newBar.Low = GetRandomNumber(Math.Min(newBar.Close, newBar.Open), Math.Min(newBar.Close, newBar.Open) - Math.Abs(newBar.Close - newBar.Open) * fluct);
newBar.Volume = (long)GetRandomNumber(newBar.Volume * volFluct, newBar.Volume);
}
Применение:
Создайте экземпляр PriceBar, заполните цены предыдущего бара. Передайте экземпляр PriceBar функции GenerateRandomBar(). Он вернет PriceBar с новыми значениями.