Избранное трейдера RuslanX
Привет, Смартлаб.
Дисклеймер: книжек не читал, теханализом никогда не увлекался, на истину не претендую, в терминах могу ошибаться, просто описываю свой первый опыт.
Примерно год назад смотрел динамику своего портфеля в сравнении с бенчмарками LQDT и MOEX. И думал, эх, тут бы всё продать, переложиться в LQDT, а вот здесь совершить обратную рокировку. Даже придумал худо-бедный алгоритм для своей торговой системы. Совершил подход — заблудился в 3-х соснах IDE для Python + нужная версия Python + нужные библиотеки. Забил.
В начале апреля запал этой идеи вновь разгорелся. Методом проб и ошибок нашёл основные столпы для своей системы:
Я уже больше 10 лет торгую на бирже, в том числе 8 лет опционами. За это время я перепробовал все виды трейдинга от скальпинга до дивидендного инвестирования, все виды опционных стратегий от голой покупки до продажи волатильности. В итоге для большей части своего портфеля я выбрал стратегию продажи покрытых опционов.
В чем суть стратегии? Как ее применять?
Шаг 1
Выбор и покупка качественного актива.
Ваша задача выбрать высококачественный актив, который вы хотите купить прямо сейчас. Вас устраивает текущая цена и фундаментальные показатели.
Например, вы покупаете ETF на биткоин — IBIT, по цене 65 ($6500).
Шаг 2
Выбор и продажа опциона.
В зависимости от вашей вовлеченности в рынок вы можете выбрать колл-опционы со сроком экспирации от недели до года. Цена страйк опциона обычно выбирается чуть выше текущей цены актива. А если вы сильно верите в рост актива, то значительно выше текущей цены. Но лучше не заниматься прогнозами, а всегда выбирать центральный страйк, то есть наиболее близкий к текущей цене.
В предыдущих статьях я рассказывал, как пришёл к идее создания собственного торгового робота. Мотивация проста:
Автоматизация — алгоритм не спит, не нервничает и не занят своими делами.
Дисциплина — робот исключает эмоции, следуя правилам.
Тестирование — любую идею можно проверить на исторических данных, прежде чем рисковать деньгами.
Я всегда разделял два этапа: разработку торговых идей (логика стратегии) и реализацию механизма исполнения (отправка заявок, автотрейдинг). Сначала — бэктестинг и базовая оптимизация, и только потом — реальная торговля.
Поскольку я нахожусь в активном поиске подходящего решения для автотрейдинга и уже опробовал несколько рабочих вариантов, то эта статья представляет мои размышления об этом механизме исполнения заявок. Ваша критика или поддержка идей приветствуется.
Почему я не хочу использовать QUIК и Windows?
По моему мнению QUIK архаичен, нестабилен для автоматизации и требует оконной среды. Он не предназначен для headless-серверов (это компьютер без монитора, клавиатуры, мыши). QUIK + LUA или внешнее ПО — это сложная, криво документированная и уязвимая связка.
Продолжаю радовать вас и себя свежими подборками корпоративных облигаций из числа уже торгующихся на бирже, к которым можно присмотреться для получения регулярного купонного дохода.
🔥Сегодня — горячая подборка надежных плавающих бондов, которые платят купоны КАЖДЫЙ месяц! Почти как зарплату, только на работу при этом ходить не нужно😎
Подписывайтесь на телеграм-канал, где регулярно выкладываются стратегии инвестирования и актуальные подборки инструментов.

💼Вначале повторюсь, что для консервативной части портфеля в 2025 году лучшим выбором, на мой взгляд, могут стать следующие инструменты:
👉Вклады под максимально высокую ставку и желательно ещё с дополнительными бонусами;
👉Фонды денежного рынка для временной парковки кэша на брокерском счете (например, фонд LQDT);
👉И, конечно же, облигации: с фиксированным купоном — для «бронирования» высокой ставки на какое-то время вперед, а также флоатеры — на случай, если ДКП останется жесткой ещё надолго.
Сегодняшние критерии такие:● Погашение от 1 года до 4 лет;
Для анализа будем использовать данные ETF c базовой валютой USD: FXCN, FXRL, FXIT, FXUS и FXRU. Временной ряд рассмотрим за три года с 2018 по 2020 года. Само исследование проведем в Google Colaboratory.
Как обычно в начале импортируем все необходимые библиотеки для дальнейшей работы.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
import warnings
warnings.filterwarnings("ignore")Сначала необходимо получить данные. Есть несколько способов. Мы воспользовались — взяли их с Finam в формате csv. Дальше написал функцию для обработки полученных данных и при помощи concat свел их в один датафрейм.def changeDF(df): df['date'] = pd.to_datetime(df['<DATE>'].astype(str), dayfirst=True) name =[x for x in globals() if globals()[x] is df][0] df = df.drop(['<DATE>','<TIME>', '<OPEN>', '<HIGH>', '<LOW>'], axis=1) df = df.set_index(['date']) df.columns = [name+'_cl', name + '_vol'] return df fxgd_change = changeDF(fxgd) fxrl_change = changeDF(fxrl) fxit_change = changeDF(fxit) fxus_change = changeDF(fxus) fxru_change = changeDF(fxru) fxcn_change = changeDF(fxcn) etf = pd.concat([fxgd_change, fxrl_change, fxit_change, fxus_change, fxru_change, fxcn_change], axis=1) etf.head()В результате получили:
import sqlite3 as sql
from scipy.stats import logistic
import math
import numpy as np
import numpy.random as rnd
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPRegressor
sdata =[]
sql1= "select ticker, date, open, high, low, close, vol \
from Hist_1m where ticker_id=1 order by Date;"
con=sql.connect('C:/Users/ubase/Documents/StockDB/StockDB21.sqlite')
cur=con.cursor()
cur.execute(sql1)
sdata=cur.fetchall()
con.commit()
con.close()
Ldata = len(sdata)
N = 8000 # Количество сделок
ld = 5 #Продолжительность сделки
NNinterval = 20 # Количество входов NN
# Генерация случайных чисел
rng = rnd.default_rng()
rm=rng.integers(0, Ldata, N )
class Candle:
tr = 0
dt = 1
o = 2
h = 3
l = 4
c = 5
v = 6
cl = Candle
DataC =[sdata[i][cl.c] for i in range(0,Ldata)]
# sigmoid линейность до 0.5
def sigmoidnorm(x, alfa = 0.9, xmin = -1.3, xmax = 1.3):
return (xmax - xmin)*((1 / (1 + math.exp(-x*2.0*alfa))) - 1.0) + xmax
x = [0.002 * i - 3 for i in range(0,3000)]
y = [sigmoidnorm(x[i]) for i in range(len(x))]
plt.plot(x,y)
plt.grid()
plt.show()
# формируем сделки.
def DealsGenL(rm,ld):
#Lm = len(rm)
ix = []
x = []
pr = []
for i in range(0,N):
if rm[i] + ld < Ldata and rm[i] - NNinterval - 1 > 0:
delta = (sdata[rm[i]+ld][cl.c] - sdata[rm[i]][cl.c])/sdata[rm[i]+ld][cl.c]*100
x0 = [sigmoidnorm((sdata[rm[i] - j][cl.c] - sdata[rm[i]][cl.c])/sdata[rm[i]][cl.c]*100) \
for j in range(0, NNinterval)]
ix.append(rm[i])
x.append(x0)
pr.append(delta)
return ix, x, pr
Ix, X, Pr = DealsGenL(rm,ld)
Ib = 0
Ie = 100
plt.plot(X)
plt.legend()
plt.grid()
plt.show()
plt.plot(Pr, label = 'Prof')
plt.legend()
plt.grid()
plt.show()
regr = MLPRegressor(hidden_layer_sizes = [30,20,15,10,5], \
max_iter=500, activation = 'tanh')
regr.fit(X, Pr)
Out = regr.predict(X)
plt.plot(Pr, Out, '.')
plt.grid()
plt.show()И вот результат прогнозирования:--
--СКРИПТ Niki для smart-lab.ru 260321 ревизия
---------------------------------------
-- Флаг для поддержания работы функции main
is_run=true
fut_limit_old =0
fut_limit_max =0
kgo_old =0.5
function main( ... ) -- чудотворная функция внутри которой все работает
--"r": режим чтения (по умолчанию);
--"w": режим записи;
--"a": режим добавления;
--"r+": режим обновления, все предыдущие данные сохраняются;
--"w+": режим обновления, все предыдущие данные стираются;
--"a+": режим добавления и обновления, предыдущие данные сохраняются, запись разрешена только в конец файла. b бинарные файлы
-- Пытается открыть файл в режиме "чтения/записи"
f = io.open(getScriptPath().."\\Limits.txt","a");
-- Если файл не существует
if f == nil then
-- Создает файл в режиме "записи"
f = io.open(getScriptPath().."\\Limits.txt","w");
-- Закрывает файл
f:close();
-- Открывает уже существующий файл в режиме "чтения/записи"
f = io.open(getScriptPath().."\\Limits.txt","a");
end;
while is_run do
sleep(1000) -- 1000 = 1 секунда --волшебная пауза в работе скрипта
if getFuturesLimit("A111", "A111111", 0, "SUR") ~= nil then -- защита от пустых таблиц -- впишите ваши данные из Квика
-- %c - дата и время (по-умолчанию) (пример, 03/22/15 22:28:11)
-- %x - дата (пример, 09/16/98)
-- %X - время (пример, 23:48:10)
seconds = os.time(); -- в seconds будет значение 1427052491
date1 = os.date("%x",seconds); -- %c - дата (по-умолчанию) (пример, 03/22/15 22:28:11)
time1 = os.date("%X",seconds); -- %c - время (по-умолчанию) (пример, 03/22/15 22:28:11)
--[[
liquidity_coef --NUMBER Коэффициент ликвидности
cbp_prev_limit --NUMBER Предыдущий лимит открытых позиций на спот-рынке»
cbplimit --NUMBER Лимит открытых позиций
cbplused --NUMBER Текущие чистые позиции
cbplplanned --NUMBER Плановые чистые позиции
varmargin --NUMBER Вариационная маржа
accruedint --NUMBER Накопленный доход
cbplused_for_orders --NUMBER Текущие чистые позиции (под заявки)
cbplused_for_positions --NUMBER Текущие чистые позиции (под открытые позиции)
options_premium --NUMBER Премия по опционам
ts_comission --NUMBER Биржевые сборы
kgo --NUMBER Коэффициент клиентского гарантийного обеспечения
currcode --STRING Валюта, в которой транслируется ограничение
real_varmargin --NUMBER Реально начисленная в ходе клиринга вариационная маржа. Отображается с точностью до 2 двух знаков. При этом в поле «varmargin» транслируется вариационная маржа, рассчитанная с учетом установленных границ изменения цены
--]]
fut_limit = getFuturesLimit("A111", "A111111", 0, "SUR").cbplused_for_positions -- NUMBER Текущие чистые позиции (под открытые позиции) -- впишите ваши данные из Квика
varmargin = getFuturesLimit("A111", "A111111", 0, "SUR").varmargin -- впишите ваши данные из Квика
accruedint = getFuturesLimit("A111", "A111111", 0, "SUR").accruedint -- впишите ваши данные из Квика
ts_comission = getFuturesLimit("A111", "A111111", 0, "SUR").ts_comission -- впишите ваши данные из Квика
kgo = getFuturesLimit("A111", "A111111", 0, "SUR").kgo -- впишите ваши данные из Квика
profit = varmargin + accruedint;
--if math.abs(fut_limit-fut_limit_old) > 10000 then -- каждые 10000 рублей изменения ГО, слишком частый файл печати
if math.abs(fut_limit-fut_limit_old) > 100000 then -- каждые 100000 рублей изменения ГО, настраиваем под себя.
open_lim = getFuturesLimit("A111", "A111111", 0, "SUR").cbplimit --NUMBER Лимит открытых позиций
f:write( tostring(date1).." "..tostring(time1).." ".."ГО: "..tostring(fut_limit).." ".."Профит: "..tostring(profit).." ".."Комис: "..tostring(ts_comission).." ".. "КГО: "..tostring(kgo).." Lim: "..tostring(open_lim).. "\n"); -- "\n" признак конца строки
--f:write( tostring(date1).. " " ..tostring(time1).. " " .. "BID: " .. tostring(res_trans) .. " " .. "ASK: " .. tostring(MXU8ask_vol) .. "\n"); -- "\n" признак конца строки
-- Сохраняет изменения в файле на диск
f:flush();
fut_limit_old = fut_limit;
end
if fut_limit_max == 0 then
fut_limit_max = fut_limit;
end
if ( math.abs(fut_limit-fut_limit_max) > 1000000 and fut_limit>0 ) then -- настраиваем под себя
message( tostring(fut_limit) ) ----сообщение в Квик--
--message( tostring(time1) )
---------------------------------------- отправляем сообщение в Телеграмм--
pos_free = getFuturesLimit("A111", "A111111", 0, "SUR").cbplplanned --NUMBER ГО свободных денег от позы без пониженного ГО
open_lim = getFuturesLimit("A111", "A111111", 0, "SUR").cbplimit --NUMBER Лимит открытых позиций
tg_message = tostring(open_lim).." ГО:"..tostring(fut_limit).." Поза:"..tostring(open_lim-pos_free)
os.execute('curl "https://api.telegram.org/botВашиДанныеИзТелеграмм&text= + '..tg_message..' " ') -- отправляем в телегу, через винду. Вписать ваши данные из Телеграмм
----------------------------------------
-- Пример строки https://api.telegram.org/bot365877050:AAE232342348HIqifnyGSsw89U_4TK3Y/sendMessage?chat_id=202560128&text= + Привет Квик!
----------------------------------------
fut_limit_max = fut_limit;
end
if math.abs(kgo-kgo_old) > 0 then
---------------------------------------- отправляем сообщение в телеграмм
tg_message = tostring(kgo).." Внимание! Изменился коэффициент КГО"
os.execute('curl "https://api.telegram.org/botВашиДанныеИзТелеграмм&text= + '..tg_message..' " ') -- отправляем в телегу, через винду. Вписать ваши данные из Телеграмм
----------------------------------------
-- Пример строки https://api.telegram.org/bot365877050:AAE232342348HIqifnyGSsw89U_4TK3Y/sendMessage?chat_id=202560128&text= + Привет Квик!
----------------------------------------
kgo_old = kgo;
end
end
end
f:close(); -- закрываем файл печати.
end
-- Остановка скрипта из Квика
function OnStop(stop_flag)
is_run=false
endВсем здоровья и бодрого расположения духа!
В статье «Визуализация рекомендаций Романа Андреева на Python» мы разобрали как можно с помощью нескольких строк кода на Питоне разобрать текст, который выкладывает каждое утро в своем блоге Роман Андреев (далее по тексту Роман) — известный трейдер и блогер (или наоборот), и отобразить эти рекомендации в виде уровней и зон на графиках. В этом топике я покажу способ для извлечения информации из графических изображений с помощью технологий компьютерного зрения (но без использования нейронных сетей) на примере таблиц-рекомендаций из блога Романа Андреева.

Надеюсь, что я не напугал читателей термином «компьютер вижн», скоро вы поймете, что это просто. И что любой юный прогер может написать код для распознавания внешними камерами номеров автомобилей, который впоследствии возненавидят все автолюбители мегаполисов, а МАДИ и ГИБДД будут собирать со всех нас миллиардные штрафы
