Перейти к содержимому

Exdeath's блог

  • записи
    3
  • комментариев
    48
  • просмотра
    67 754

Записи в этом блоге

Exdeath

Данную статью в первую очередь постарался писать в расчёте на новичков даже тех кто раньше никогда не пользовался Linux. (хотя возможно будет полезна и не только новичкам т.к. в некоторых местах содержит несколько неочевидных деталей).

Если Линукс не установлен(если установлен, то можно пропустить эту часть):

Скачать последний стабильный релиз:

http://mirror.yandex.ru/ubuntu-releases/12.04.2/

(По своему опыту советую ставить именно стабильный LTS-релиз, а не 12.10 или 13.04)

Доступна как прямая закачка так и через BitTorrent.

i386 -- 32 бит, amd64 -- 64 бит

desktop -- система загрузится в режиме LiveCD и установка будет происходить оттуда. alternate -- это образы с текстовым установщиком, очень полезны при устаноке на старое железо(явно не наш случай), также она будет интересна тем, кто хочет и умеет выполнить более тонкие настройки при установке.

Если устанавливаете в первый раз, рекомендую desktop.

После закачки три варианта дальнейших действий:

1) Записать образ на DVD-болванку и загрузка с неё. (http://help.ubuntu.ru/wiki/unetbootin#настройка_параметров_bios)

2) Делаем загрузочную флешку с помощью UNetbootin и скаченного образа. (http://help.ubuntu.ru/wiki/unetbootin#настройка_параметров_bios )

3) Для гиков: установка на голое железо через виртуальную машину:

sudo kvm -m 512 -cdrom /путь_к_образу/образ.iso /dev/sda -boot d

(с помощью ключа -m указывается количество оперативки которое будет выделено эмулятору).

Независимо от способа, во время установки желательно иметь рабочий подключённый интернет. Он потребуется для докачки полной локализации и несвободных пакетов.

Сама установка довольно интуитивно понятна, а в её начале можно выбрать практически любой из языков мира.

Единственный неочевидный момент это разбивка диска. Я рекомендую выполнить её вручную перед запуском установки при помощи программы Gparted(уже будет инсталирована на LiveCD-системе). А при установке выбрать пункт "разбивка вручную" и указать точки монтирования.

Самый простой вариант: отрезать с помощью Gparted кусок свободного места и создать там новый раздел с файловой системой по вкусу. А при установке назначить разделу точку монтирования "/" (корневой каталог). Если оперативки меньше 2 гигов, желательно создать ещё и раздел подкачки (swap).

 

По умолчанию используется ext4. Если планируете использовать там "толстый" биткоин-клиент, то имеет смысл использовать ФС Reiser4(нету в ядре, нужно доустановить) возможно лучше подойдёт для более быстрой синхронизации базы. Также очень неплохую производительность имеет Btrfs, хотя многие считают её до сих пор "сырой". В любом случае не забывайте про своевременный бэкап важных файлов.

 

Установщик Wubi(для установки из под Windows) я не рекомендую он не производит установку на полноценный отдельный раздел, используйте его только в крайнем случае, если не умеете выполнять разбивку диска.

 

После окончания установки уходим в ребут.

При необходимости настраиваем русскую раскладку:

 

 

Внимание! Если система установлена на русском, пишем "раскладка" и "клавиатура" на русском (в 12.04(не 12.04.2) был баг(или неправильная фича), когда после установки с выбором русского языка были 4 русских раскладки(разных версий) и ни одной английской. Нужно было удалить лишние русские раскладки и добавить английскую). Если в 12.04.2 этот баг исправили(при установке с русской локализацией будут две раскадки: русская и английская), то это этап постустановочной донастройки не нужен.

 

0_ab2e7_743b5ffe_orig.png

0_ab49e_379913c1_orig.png

0_ab2e8_72dc04e1_orig.png

0_ab2e9_7861fefd_orig.png

 

 

 

Переходим непосредственно к настройки майнинга.

 

 

Устанавливаем проприетарные драйвера на видеокарту:

Также, как и в случае с раскладкой Если система на русском, то в поиске пишем 'др' вместо 'dr'

0_ab2da_9eae2b62_orig.png

Ставим стабильную версию дров. Никаких экспериментальных версий!

Выбираем версию выделенную на скриншоте и жмём "активировать".

0_ab2df_58d3471c_orig.png

 

Открываем эмулятор терминала:

Пишем в поике "terminal" или "терминал" (в зависимости от языка системы). Когда он откроется, желательно закрепить кнопку для его запуска на левой панели(правая клавиша мыши по значку и выбор соответствующего пункта меню).

Обновляем систему:

sudo apt-get dist-upgrade

Потребуется пароль, который был выбран при установке, при его вводе звёздочки отображаться не будут.

Если пароль введён верно, будет запрошено подтверждение, вводим 'y'.

0_ab2de_d08a8573_orig.png

Приписка dist означает, что ядро тоже нужно обновить.

Установка проприетарных дров и обновление ядра это единственное на что система может попросить перезагрузку.

 

Устанавливаем дополнительные пакеты:

sudo apt-get install fglrx g++ libboost-all-dev python-numpy thunar openbox tint2 obmenu menu

Последние пакеты позволят запусть майнинг не в Unity, а в более легковесной среде Openbox, которая позволить "выжать" из рабочей видеокарты практически столько же мегахешей, сколько дают карты без монитора. Или позволит комфортно работать вплоть до просмотра видео в FullHD, при совсем незначительном снижении агрессии или интенсивности на рабочей карте.

Thunar -- легковесный файловый менеджер, замена имеющемуся по умолчанию Nautilus. Несмотря на легковесность имеет пару серьёзных преимществ.

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

Часто таким способом вместо использования команды "cd" можно быстрее перейти в нужный каталог в терминале.

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

 

Прокомментирую прочие отличия по сравнению софтом, который предлагается установить в старой статье.

Мы не будем ничего устанавливать из git или subversion, поэтому эти пакеты в данном случае не нужны. Хотя при необходимости их конечно же можно установить.

А установку openssh-server, я вообще посчитал очень вредным советом. Во-первых совсем не факт, что потребуется удалённый доступ к своей машине, а защищённость машины такая установка явно снизит. Iptables за вас сам не настроится, даже самой элементарной защиты от брутфорса по ssh по умолчанию в ubuntu нет. Во-вторых, если удалённый доступ всё же необходим, то я настоятельно рекомендую не ограничиться установкой OpenSSH. Рекомендую отключить авторизацию через пароль и использовать для неё асимметричную пару ключей с парольной фразой.

Как настроить авторизацию по ключу можно прочитать в статье «Памятка пользователям ssh» на Хабре.

 

Минимальная настройка iptables с политикой "всё запрещено по-умолчанию":

iptables -Fiptables -t nat -Fiptables -t mangle -Fiptables -Xiptables -t nat -Xiptables -t mangle -Xiptables -P INPUT DROPiptables -P FORWARD DROPiptables -P OUTPUT DROP#OPEN_INPUT_TCP_PORTS="";iptables -A INPUT -i lo -j ACCEPTiptables -A OUTPUT -o lo -j ACCEPTiptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTiptables -A OUTPUT -m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT#iptables -A INPUT -p tcp -m multiport --dport $OPEN_INPUT_TCP_PORTS -j ACCEPT

Нужно скопировать в файл /etc/rc.local в любое место перед строкой "exit 0".

(открытие файла через gedit: sudo gedit /etс/rc.local)

Если нужно открыть некоторые порты для входящих соединений, то нужно раскомментировать закомментированные строки и перечислить их через запятую в переменной OPEN_INPUT_TCP_PORTS. Например доступа к веб-серверу из интернета нужно открыть 80-ый порт, а для ssh-сервера 22-ой(по умолчанию). Для некоторых приложений, таких как bitcoin-qt или Torrent-клинет открытие соответствующего порта не обязательно, но позволит увеличить количество подключений.

 

 

Что касается vim'а, то он перенесён в список рекомендуемого софта:

sudo apt-get install vim unrar p7zip-full mplayer enca mc gimp gmrun scrot htop deluge-gtk deluged deluge-console gdebi

etc.

С готовым конфигом и знанием пользователем горячих клавиш Mplayer становиться просто идеальным видеопроигрывателем.

Утилита enca, служащая для автоматического определения кодировок, прописана в моём варианте конфига mplayer и служит для автоматического определения кодировки субтитров.

 

 

 

Скачиваем AMD SDK 2.7:

http://developer.amd.com/tools/heterogeneous-computing/amd-accelerated-parallel-processing-app-sdk/downloads/download-archive/

(скачивайте именно 2.7, а не 2.8)

Скачиваем исходники pyopencl:

http://pypi.python.org/packages/source/p/pyopencl/pyopencl-2011.2.tar.gz

Cкачиваем python-jsonrpc:

https://eclipsemc.com/files/python-jsonrpc.tar.gz

 

Распаковываем содержимое архивов в любые, но очень желательно разные каталоги.

Каким именно способом значения не имеет. Это можно сделать как через GUI:

0_abe6d_4b20ba6_orig.png

Так и через CLI:

tar xvfz архив -C в_какой_каталог_распаковать

"архив" -- это полный или относительный путь до архива+имя (путь/имя_архива)

Следует знать следующие обозначения:

. - текущий каталог

.. - родительский каталог

Пример относительного пути: ../../somedir/filename.tgz

Если архив лежит в текущем каталоге достаточно просто указать имя архива.

(Фомат ./имя_файла обязателен только когда файл запускается на исполнение).

 

 

Установка AMD SDK:

Выполняем скрипт "Install-AMD-APP.sh" от имени рута(root -- специальная учёная запись с неограниченными правами):

username@systemname:~/путь-к-каталогу$ sudo ./Install-AMD-APP.sh

где "путь-к-каталогу" -- путь к каталогу в котором лежит содержимое распакованного архива относительно домашней папки(/home/имя_пользователя), которая обозначается знаком "~". ~/путь-к-каталогу -- это полный путь т.е. путь относительно корневого каталога.

Например если содержимое распаковано в ~/Downloads/AMD_APP_SDK, то открываем терминал и выполняем:

username@systemname:~$ cd ~/Downloads/AMD_APP_SDKusername@systemname:~/Downloads/AMD_APP_SDK$ sudo ./Install-AMD-APP.sh

Также можно сразу открыть в нужном каталоге через Thunar:

Открываем контекстное меню правой кнопкой мыши(клик в пустом месте, не на файле):

0_acf53_4860ef44_orig.png

0_acf52_55f1be0d_orig.png

Установка произойдёт автоматически в каталог /opt/AMDAPP

Кроме запука скрипта под рутом никаких дополнительных действий производить не нужно.

 

Переходим в каталог с распакованным pyopencl любым из способов.

 

Компиляция и установка pyopencl из исходного кода:

./configure.py --cl-inc-dir=/opt/AMDAPP/include/ --cl-lib-dir=/opt/AMDAPP/lib/x86_64

или

./configure.py --cl-inc-dir=/opt/AMDAPP/include/ --cl-lib-dir=/opt/AMDAPP/lib/x86

в зависимости от битности системы.

компилируем:

make

устанавливаем:

sudo make install

Переходим в каталог с распаковым python-jsonrpc любым из способов.

Установка python-jsonrpc:

sudo python setup.py install

Если стоит несколько видеокарт для майнинга, по умолчанию майнинг на видеокартах к которым не подключены мониторы запускаться не будет.

Я решил данный вопрос на уровне железа(подробности ниже). Хотя я более чем уверен, что вопрос можно решить и на уровне софта.

Для способа, приведённого ниже нужно иметь или хотя бы один дополнительный монитор(даже если видеокарт больше) или возможность сымитировать хотя бы 1 дополнительный монитор например вот таким способом:

terminator.png

Картинка позаимствована с сайта btcsec.com на ней изображены сопротивления в 75 Ом.

По возможности лучше воткнуть мониторы(подойдут даже очень древние) или их имитации во все видеокарты, но если такой возможности нет,

можно обойтись пока что только второй.

 

После установки драйверов в списке системных программ должен был появиться AMD Catalyst Control Center. Запускаем его:

0_ab2e2_98bc7214_orig.png

Из консоли его можно запустить командой "sudo amdcccle" или "gksudo amdcccle".

Через него карты можно будет "активировать":

0_abba3_a97950cc_orig.png

и майнинг на них будет работать даже после отключения мониторов от них.

Т.е. хватит только одного дополнительного монитора, его просто нужно будет поочерёдно подключать к каждой карте,

перезагружаясь между "активациями" каждой из них. В результате чего майнинг будет работать на всех картах и можно будет оставить только один рабочий монитор.

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

После действий описанных выше майнинг на системе будет успешно запускаться.

 

 

Update: В крайнем случае, если у вас несколько карт, но нет второго монитора или заглушек, а достать второй монитор негде даже на время, то можете попробовать поставить свежую версию драйверов:

http://support.amd.com/us/gpudownload/linux/Pages/radeon_linux.aspx?type=2.4.1&product=2.4.1.3.42&lang=English

По этой инструкции: http://habrahabr.ru/post/179279/

И если это помогло, то просьба отписаться.

 

Теперь можно скачивать любой майнер, настроить его и подключиться к любому пулу.

Например Cgminer можно скачать здесь:

https://bitcointalk.org/index.php?topic=28402.0

http://ck.kolivas.org/apps/cgminer/

Информацию по его настройке можно получить в теме на форуме:

https://forum.bits.media/index.php?/topic/239-cgminer/?hl=cgminer

Единственное отличие от настройки под windows заключает в отсутствии расширения "exe" и синтаксисе запуска исполняемого файла.

Например вместо:

cgminer.exe -o pool.com:8332 -u vasya.pupkin@gmail.com -p password

Будет:

./cgminer -o pool.com:8332 -u vasya.pupkin@gmail.com -p password

Файл cgminer должен иметь атрибут исполняемого файла.

Если файл не помечен как исполняемый, то он не запуститься. Это можно исправить так:

Через терминал:

chmod +x cgminer

Проверить наличие 'x':

ls -l каталог_с_майнером

Если вы уже находитесь в каталоге с майнером, то вместо "ls -l ." достаточно написать просто "ls -l".

 

или

 

Через свойства файла на примере Thunar:

0_af04d_a4862ec5_orig.png

 

Лично использую phoenix 1.7.5 просто потому, что его внешний вид для меня более привычен, поэтому свою настройки покажу на примере его, несмотря на то, что cgminer более функционален.

Лично я создал на пулах(если один из пулов ляжет можно подключиться к другому) по отдельному воркеру на каждую карту.

Это не рекомендация, а просто дело вкуса, например в конфиге cgminer'а можно прописать сразу все карты.

Скрипты для запуска воркеров я создал в каталоге ~/bin/ и сделал их исполняемыми командой "chmod +x".

Пример содержимого моего воркера:

#!/bin/bashcd /home/exdeath/bin/phoenixpython phoenix.py -u http://exdeath_1:password@pool.itzod.ru:8344 -k phatk BFI_INT FASTLOOP VECTORS AGGRESSION=11 WORKSIZE=128 DEVICE=1

Подробную инструкцию о подключении к p2pool я добавлю чуть позже.

 

Последние штрихи оптимизации, выжимаем из карт больше мегахешей:

 

Для того, чтобы сменить графическию среду с Unity на Openbox сначала нужно разлогиниться:

0_ac0fb_160d2ebb_orig.png

и находясь в LightDM сменить Unity на Openbox:

0_ac0fd_592cd696_orig.png

0_ac0fc_b6fd5c62_orig.png

 

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

Подробнее о настройке Openbox можно почитать например здесь:

https://syslinux.ru/node/608

 

Если кратко:

Настройки лежат в каталоге ~/.config/openbox

Точка в начале имени каталога делает его скрытым. Включить/выключить отображение скрытых файлов в графическом файловом менеджере можно при помощи сочетания Ctrl+h.

autostart или autostart.sh -- скрипт для автозапуска

menu.xml -- настройка меню

rc.xml -- прочие настройки, в частности настройки горячих клавиш

Если каких-то файлов нету, то их можно создать(насколько я помню, по-умолчанию нет файла для автозапуска).

 

Если допустить в menu.xml синтаксическую ошибку, то после реконфигурацию насколько я помню оно отображаться не будет.

К счастью файл можно править не только через текстовый редактор, но и через специальную графическую утилиту под названием obmenu.

Сравнение стандартного меню и "прокаченного":

0_ac129_43cbd1b1_orig.png

0_ac140_34b15856_orig.png

 

В частности пункты для запуска майнинга:

0_ac2e6_b8c4aa47_orig.png

 

Настраиваются вот так:

0_ac2e5_616b831d_orig.png

 

Например у меня autostart в данный момент выглядит так:

#feh --bg-scale /home/exdeath/.4lightdm.png &setxkbmap -layout "us,ru(winkeys)" -model pc105 -option grp:alt_shift_toggle,grp_led:scroll,compose:ralt &tint2 &#настраиваем грокость звукаamixer -c 0 sset PCM,0 100%amixer -c 0 sset Master,0 100% #*amixer -c 0 sset Line,0 100%#amixer -c 0 sset PCM,0 80%#amixer -c 0 sset Master,0 90%#amixer -c 0 sset Line,0 70%aticonfig --pplib-cmd "set fanspeed 0 100"export DISPLAY=:0.1; aticonfig --pplib-cmd "set fanspeed 0 100"export DISPLAY=:0.2; aticonfig --pplib-cmd "set fanspeed 0 100"amdconfig --od-enableaticonfig --od-setclocks=950,280 --adapter=all

Первая строчка раньше отвечала за отображение обоев(сейчас я её закомментировал). Если обои нужны, то кроме раскомментирования строчки не забудте установить пакет "feh" использовать адрес имеющемуся на компьтере изображению.

Вторая строчка настраивает клавиатуру на тот случай, если она не настроена или настроена неправильно. Индикатором раскладки будет служить лампочка Scroll lock.

Третья строчка запускает панель "tint2"

 

И наконец в конце используется непосредственно связанная с майнингом утилита amdconfig (или aticonfig, это синонимы).

Перед её использование нужно выполнить

sudo amdconfig --initial -f --adapter=all

А перед тем как разгонять карту:

sudo amdconfig --od-enable

Различные примеры использования уже написаны в старой статье, поэтому я просто процетирую:

Чтобы отобразить температуры на Ваших ядрах, выполните:

 

aticonfig --odgt --adapter=all

 

Для отображения тактовой частоты на Ваших ядрах:

aticonfig --odgc --adapter=all

 

Отобразить или задать скорость вращения кулера.

 

Показать скорость вращения кулера:

 

aticonfig --pplib-cmd "get fanspeed 0"

 

Задать скорость вращения кулера 100%:

 

aticonfig --pplib-cmd "set fanspeed 0 100"

 

Показать скорость куллера на второй карте:

 

export DISPLAY=:0.1; aticonfig --pplib-cmd "get fanspeed 0"

 

Вы можете заменить 1 на 2, 3 и т.д. в соответствии с количеством видеокард в системе.

 

Смена тактовых частот. Чтобы задать уровень тактовой частоты ядра 900MHz и тактовую частоту памяти 1000MHz на всех картах (можете изменять по Вашему желанию):

 

aticonfig --od-setclocks=900,1000 --adapter=all

Последняя строчка это ещё один вредный совет, поскольку увеличение таковой частоты памяти(в отличие от тактовой частоты ядра) никак не повлияет на добычу биткоинов. В то время как её снижение хоть немного, но снизит нагрев видеокарт и расход электроэнергии.

Exdeath

Возможно это первая часть цикла "HelloWorld для написания торгового бота", а возможно единственный HelloWorld от меня, я пока ещё точно не могу сказать. Мой пример будет на Flash ActionScript 3. Во-первых я "на ты" именно с флешем. Во-вторых, в сети уже есть множество примеров-hellworldов для Гокса и BTC-E, на многих языках, но ActionScript 3 я не видел. И в-третьих, флеш позволяет создавать интерфейсы на самый любой вкус без каких-либо ограничений. Интерфейс можно сделать удобным и практичным, а при необходимости, ещё и красивым и динамичным. Для создания бота без GUI я бы предпочёл Python или Perl. Но используя GUI, можно создать торговые инструменты с подсказками, автоматизацией и с функцией автопилота, в работу которого можно в любой момент вмешаться. По-моему, это намного лучше чем демон, работающий сам по себе.

 

Однако, в рамках данной статьи в примерах кода пока что не будет запросов на сделки и покупку/снятие ордеров, хотя это и немаловажно, при написании бота это все-таки не самый первый этап, а некий HelloWorld №2. Решения о сделках или ордерах должны приниматься на основе данных, полученных от бирж, поэтому самый первый этап это получение данных и их обработка. А как только данные из JSON будут преобразованы в переменные и/или массивы ими можно будет свободно пользоваться в программном коде для решения необходимых задач.

 

В качестве примера обработки спарсинного JSON, будет приведён исходный код графического отображения глубины торгов(как на mtgoxlive.com). Для этого нужно получить и спарить данные о глубине, полученные от биржи.

 

 

Чтобы приведённый мной код успешно скомпилировался, я рекомендую компилировать под версию флешплеера не ниже 11.2. Под 10-ю можно даже не пытаться: там нет встроенного парсера JSON, который будет использоваться в моём коде. Также будет чуток PHP-кода. Это связано с тем, что политика безопасности флешплеера не позволяет подгрузить данные с другого домена. Это обходится с помощью вебсервера и PHP.

 

Итак данные о глубине торгов биржи BTC-E по паре btc/usd расположены тут: https://btc-e.com/api/2/btc_usd/depth (ранее: https://btc-e.com/api/2/1/depth)

Чтобы они были доступны с того же домена на котором расположен swf-файл, нужно использовать следующий php-код:

<?php
$q = $_GET['q'];
if($q=="depth_btcusd")$page = file_get_contents("https://btc-e.com/api/2/btc_usd/depth");
if($q=="trades_btcusd")$page = file_get_contents("https://btc-e.com/api/2/btc_usd/trades");
if($q=="ticker_btcusd")$page = file_get_contents("https://btc-e.com/api/2/btc_usd/ticker");


if($q=="depth_btcrur")$page = file_get_contents("https://btc-e.com/api/2/btc_rur/depth");
if($q=="trades_btcrur")$page = file_get_contents("https://btc-e.com/api/2/btc_rur/trades");
if($q=="ticker_btcrur")$page = file_get_contents("https://btc-e.com/api/2/btc_rur/ticker");

#и т. д.


echo $page;
?>

Переменная q нужна, чтобы не плодить огромное количество PHP-файлов, а использовать один.

Запрос на получение глубины торгов по паре btc/usd с локального домена будет выглядеть так:

localhost/btce.php?q=depth_btcusd

 

Что касается гокса, он также отдаёт данные в формате JSON немного другим способом и использование file_get_contents не прокатает, но можно сделать так:

<?php
$q = $_GET['q'];
if($q=="depth_btcusd")$ch = curl_init('https://mtgox.com/api/0/data/getDepth.php?Currency=USD');
#и аналогично с прошлым примером


curl_setopt($ch, CURLOPT_USERAGENT, "GoxFlashViewer public alpha");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);


$page=curl_exec($ch);
curl_close($ch);
echo $page;
?>

 

В результате, на своём хосте мы получим способ выдачи и разметку JSON в абсолютно том же виде, как и для BTC-E. Для меня это было немаловажно т.к. к тому времени, как я прикрутил cURL с своему Nginx(без Apache), код для графического представления глубины для BTC-E уже был написан.

 

 

Далее приведу исходник флешки, снабжённый некоторыми комментариями в функции, строящей график, обрабатывая полученные данные:

 

package {
   import flash.display.StageAlign;
   import flash.display.StageScaleMode;
   import ru.exdeath.as3gui.WindowSkin;
   //классы Window и WindowSkin написаны мной; их код также будет приведён
   import flash.display.Sprite;
   import ru.exdeath.as3gui.Window;
   import flash.system.Security;
   import flash.events.Event;
   import flash.net.URLLoader;
   import flash.net.URLRequest;
   import flash.display.MovieClip;
   import flash.text.TextField;
   /**
    * @author exdeath
    */
   [sWF(width="700", height="580", frameRate="60", backgroundColor="#ffffff")]
   public class btce extends Sprite{
       public function btce() {

           //отключаем изменение маштабирования
           //при изменении размера рабочей области:
           stage.scaleMode = StageScaleMode.NO_SCALE;
           stage.align = StageAlign.TOP_LEFT;

           var loadComplete:Boolean=false;
           var request_depth:URLRequest = new URLRequest("http://localhost/btce.php?q=depth_btcusd");

           var loader:URLLoader = new URLLoader();


           loader.addEventListener(Event.COMPLETE, completeHandler);

           loader.load(request_depth);



           var txt:TextField = new TextField();
           txt.selectable=false;



           this.addChild(txt);

           try
           {
               loader.load(request_depth);
           }
           catch (error:ArgumentError)
           {
               txt.text="An ArgumentError has occurred.";
           }
           catch (error:SecurityError)
           {
               txt.text="A SecurityError has occurred.";
           }

           var n:uint=0;
           addEventListener(Event.ENTER_FRAME, onEnterFrame);
           function onEnterFrame(event:Event):void{
               if(!loadComplete){
                   txt.text="Загрузка.";
                   for(var i:uint=0;i<n%3;i++)txt.appendText(".");
                   n++;
               }
           }

           function completeHandler(event:Event):void {
               loadComplete=true;
               txt.text="done";
               var BtceDepth:Object = JSON.parse(loader.data);
               var BtceAsks:Array=BtceDepth.asks;
               var BtceBids:Array=BtceDepth.bids;

               creatDepthTxt();
               createDepthGraph();

               function creatDepthTxt():void{

                   var depthTxt:Window=new Window(450,300,null,"Стакан BTC-e BTC/USD");
                   addChild(depthTxt);

                   var asksField:TextField=new TextField();
                   var bidsField:TextField=new TextField();

                   depthTxt.box.addChild(asksField);
                   depthTxt.box.addChild(bidsField);

                   bidsField.x=200;
                   asksField.width=bidsField.width=200;
                   asksField.height=bidsField.height=300;
                   asksField.border=bidsField.border=true;

                   asksField.text="Предложение:\n";
                   bidsField.multiline=true;
                   bidsField.htmlText="Спрос:<br>";
                   for (var i in BtceAsks){
                       asksField.text+=BtceAsks[i][1]+" по "+BtceAsks[i][0]+"\n";
                   }
                   for (i in BtceBids){
                       if(BtceBids[i][1]>10.0)bidsField.htmlText+="<font color='#ff0000'>"+BtceBids[i][1]+" по "+BtceBids[i][0]+"</font><br>";
                       else bidsField.htmlText+=BtceBids[i][1]+" по "+BtceBids[i][0]+"<br>";

                   }
               }

               function createDepthGraph():void{
                   var depthGraphSkin:WindowSkin=new WindowSkin();
                   var depthGraph:Window=new Window(500, 300, depthGraphSkin,"Глубина торгов BTC-e BTC/USD");
                   addChild(depthGraph);

                   //сдвигаем окно из нуля, чтобы за ним было видно предыдущее
                   depthGraph.x=100;
                   depthGraph.y=200;

                   //рисуем оси координат
                   var x0:Number=250;
                   var y0:Number=290;

                   depthGraph.box.graphics.lineStyle(1);
                   depthGraph.box.graphics.moveTo(x0, 0);
                   depthGraph.box.graphics.lineTo(x0, 300);
                   depthGraph.box.graphics.moveTo(0, y0);
                   depthGraph.box.graphics.lineTo(500,y0);

                   //устанавливаем значения
                   //1px=0.01$
                   //1px=25btc
                   var stepX:Number=.01;
                   var btcStep:Number=25;

                   //узнаём крайнии значения
                   //trace(BtceBids[0][0],BtceAsks[0][0]);

                   //вычисляем среднее значение, там будет центр
                   var middle:Number=(BtceBids[0][0]+BtceAsks[0][0])/2;

                   trace(middle);

                   var AsksVector:Vector.<Number>=new Vector.<Number>(250);
                   var BidsVector:Vector.<Number>=new Vector.<Number>(250);

                   //пробегаемся по ордерам
                   for (var i in BtceAsks){
                       //смотрим
                       //цена=BtceAsks[i][0];количество=BtceAsks[i][1];
                       //прибавляем количество к соотв.значениям

                       for (var j in AsksVector){
                           if(BtceAsks[0][0]+j*stepX>BtceAsks[i][0])AsksVector[j]+=BtceAsks[i][1];
                       }
                   }
                   //аналогично для Bids
                   for (i in BtceBids){
                       for (j in BidsVector){
                           if(BtceBids[0][0]-j*stepX<BtceBids[i][0])BidsVector[j]+=BtceBids[i][1];
                       }
                   }

                   //рисуем график
                   depthGraph.box.graphics.lineStyle(2);
                   depthGraph.box.graphics.moveTo(x0, y0);
                   for (j in AsksVector){
                       //trace(AsksVector[j]);
                       depthGraph.box.graphics.lineTo(x0+j+1, y0-int(AsksVector[j]/btcStep));
                   }

                   //для Bids
                   depthGraph.box.graphics.moveTo(x0, y0);
                   for (j in BidsVector){
                       depthGraph.box.graphics.lineTo(x0-j, y0-int(BidsVector[j]/btcStep));
                   }
                   //рисуем деления колва биткоинов
                   //пока статично, потом сделать опционально
                   depthGraph.box.graphics.lineStyle(1,0xff0000);
                   var valuesY:Vector.<TextField>=new Vector.<TextField>(7);
                   for(i=0;i<7;i++){
                       depthGraph.box.graphics.moveTo(x0-3,y0-(i+1)*40);
                       depthGraph.box.graphics.lineTo(x0+3,y0-(i+1)*40);
                       valuesY[i]=new TextField();
                       depthGraph.box.addChild(valuesY[i]);
                       valuesY[i].x=x0+4;
                       valuesY[i].y=y0-(i+1)*40-8;
                       valuesY[i].text=(i+1)+"k";
                       valuesY[i].selectable=false;
                       valuesY[i].textColor=0xff0000;    

                   }


                   //считаем сколько требуется значений
                   //деление будет указывать каждое целое значение курса
                   //т.к. пока всё статично, можно заранее сказать, что 500*0.01$=5$
                   var valuesXN:int;
                   valuesXN=4;



                   //указываем значение курса в нуле
                   var currentCostTF:TextField=new TextField();

                   depthGraph.box.addChild(currentCostTF);
                   currentCostTF.text=BtceBids[0][0]+" "+BtceAsks[0][0];
                   currentCostTF.x=x0+10;
                   currentCostTF.y=y0-20;
                   currentCostTF.textColor=0xff0000;
                   currentCostTF.selectable=false;


                   //рисуем деления
                   var valuesX:Vector.<TextField>=new Vector.<TextField>(valuesXN);
                   //вычисляем местоположение самого левого деления и его значение
                   //значение: 
                   var valueMin:uint=int(middle-2.5)+1;
                   //местоположение:
                   var valueMinX:Number=(int(middle-.5)-middle+1.5)*100;
                   var valueXcurrent:Number;


                   for(i=0;i<valuesXN;i++){
                       valuesX[i]=new TextField();
                       valuesX[i].selectable=false;
                       valuesX[i].textColor=0xff0000;

                       valueXcurrent=valueMinX+i*100;

                       depthGraph.box.graphics.moveTo(valueXcurrent, y0-3);
                       depthGraph.box.graphics.lineTo(valueXcurrent, y0+3);
                       depthGraph.box.addChild(valuesX[i]);
                       valuesX[i].text=String(valueMin+i);
                       valuesX[i].y=y0-4;
                       valuesX[i].x=valueXcurrent-7;

                   }

               }


           }


       }
   }
}


 

Если вы незнакомы с синтаксисом AS3 и вам непонятна та или иная строчка, то на adobe.com есть отличнейшая документация на русском языке, к которой можно обратиться.

 

вот что получилось(кликабельное превью):

0_9f6e6_6787c284_M.png

 

 

Собираюсь выложить в веб, сразу как добавляю в код поддержку всех пар BTC-E и пар гокса с наибольшими объёмами и улучшу юзабилити. Хотя, если кто-нибудь попросит, могу выложить и текущею версию. А далее время от времени планирую добавлять функционал. Буду рад любым предложениям и идеям насчёт того, что требуется реализовать.

 

IDE на скриншоте: кроссплатформенный FDT 5 на основе Eclipse в ОС Ubuntu Linux 12.04.1 с Openbox вместо Unity.

И автономный адобовский флешплеер, который можно скачать здесь:

http://www.adobe.com.../downloads.html

И то и другое доступно под GNU/Linux, Windows и Mac OS X; как для 32-х, так и для 64-х битных версий данных систем.

Правда, для работы флешплеера на 64-битном Линуксе нужно доустановить некоторые 32-хбитные пакеты. (Какие именно можно узнать с помощью ldd)

 

Если файл запускается из локальной файловой системы, то скорее всего нужно будет сходить в каталог:

~/.macromedia/Flash_Player/#Security/FlashPlayerTrust (GNU/Linux)

C:\Users\<имя пользователя>\AppData\Roaming\Macromedia\Flash Player\#Security\FlashPlayerTrust (Windows 7)

C:\Documents and Settings\<имя пользователя>\Application Data\Macromedia\Flash Player\#Security\FlashPlayerTrust (Windows XP)

/Users/<имя пользователя>/Library/Preferences/Macromedia/Flash Player/#Security/FlashPlayerTrust (Mac OS X)

 

Создать там файл с любым именем и расширением .cfg или воспользоваться уже созданным *.cfg

И прописать путь к каталогу из которого запускается swf-файл

У меня прописано так:

//home/exdeath/Dropbox/workspace/fdt/btc/bin/

/home/exdeath/Dropbox/workspace/fdt/btc/bin/

 

 

И как и обещал привожу текущие версии моих классов Window и WindowSkin:

package ru.exdeath.as3gui {
   import flash.text.TextField;
   import flash.events.MouseEvent;
   import flash.display.Sprite;
   /**
    * @author exdeath
    */
   public class Window extends Sprite{

       public var box:Sprite = new Sprite;
       private var title:Sprite = new Sprite;
       private var TextInTitle:TextField=new TextField();
       //public var defaultSkin:WindowSkin = new WindowSkin();

       public function Window(width:Number,height:Number,skin:WindowSkin=null,titletext:String=""){
           //рисуем
           //var title:Sprite = new Sprite;

           if(skin==null)skin=new WindowSkin();
           //var titleH:Number=20;

           this.addChild(title);
           this.addChild(box);

           title.graphics.beginFill(skin.titleColor,skin.titleAlpha);
           title.graphics.lineStyle(skin.titleBorder,skin.titleBorderColor,skin.titleBorderAlpha);
           title.graphics.drawRect(0, -skin.titleH, width, skin.titleH);
           title.graphics.endFill();

           box.graphics.beginFill(skin.boxColor,skin.boxAlpha);
           box.graphics.lineStyle(skin.boxBorder,skin.boxBorderColor,skin.boxBorderAlpha);
           box.graphics.drawRect(0,0,width,height);
           box.graphics.endFill();

           //сдвигаем на высоту title
           this.y+=skin.titleH;

           //делаем окошки перетаскиваемыми
           title.addEventListener(MouseEvent.MOUSE_DOWN, mousePress);
           title.addEventListener(MouseEvent.MOUSE_UP, mouseRelease);
           function mousePress(e:MouseEvent):void{
               startDrag();

           }
           function mouseRelease(e:MouseEvent):void{
               stopDrag();
           }

           //помещаем в верхний слой, когда активно

           //при клике по окну, поднимаем его наверх (a=при нажатии по title; b=при нажатии по всему окну)
           //потом надо будет сделать булеву переменную в WindowSkin

           addEventListener(MouseEvent.MOUSE_DOWN, win2top); //b
           /*
           title.addEventListener(MouseEvent.MOUSE_DOWN, win2top); //a
           doubleClickEnabled = true;
           addEventListener(MouseEvent.DOUBLE_CLICK, win2top);
           */

           //создаём текстовое поле, в котором будет отображаться название окна

           title.addChild(TextInTitle);
           TextInTitle.selectable=false;
           TextInTitle.textColor=skin.textInTitleColor;
           TextInTitle.text=titletext;
           TextInTitle.x=0;//skin.titleH;//отступ для красоты
           TextInTitle.y=-skin.titleH;
           TextInTitle.width=this.width;



       }
       private function win2top(e:MouseEvent):void {
           parent.setChildIndex(this, parent.numChildren-1);
       }
   }
}

package ru.exdeath.as3gui {
   /**
    * @author exdeath
    */
   public class WindowSkin extends Object{
       public var titleH:Number=15;
       public var titleColor:Number=0x000000;
       public var titleAlpha:Number=1;
       public var titleBorder:Number=1;
       public var titleBorderColor:Number=0x000000;
       public var titleBorderAlpha:Number=1;
       public var boxColor:Number=0xFFFFFF;
       public var boxAlpha:Number=1;
       public var boxBorder:Number=1;
       public var boxBorderColor:Number=0x000000;
       public var boxBorderAlpha:Number=1;

       public var textInTitleColor:Number=0xFFFFFF;

   }
}

×