الهامبخشهای توسعه سرویس آمار برای سرمایهگذاری Tinkoff عبارتند از:
- مقاله ای در هابره «آنچه که سرمایه گذاری Tinkoff نمی گوید»
- تجزیه و تحلیل خواسته های کاربران پلت فرم
- مقاله ای در مورد محاسبه کمیسیون .
- چه چیزی مورد بحث قرار خواهد گرفت؟
- توسعه یک سرویس آمار گام به گام:
- اتصال به Tinkoff Invest API
- کشیدن داده ها از Tinkoff Invest API در یک مرورگر
- دریافت گزارش کارگزاری و معاملات
- GetBrokerReport
- روش بدست آوردن تاریخ با در نظر گرفتن تفریق از تاریخ فعلی
- درخواست تولید گزارش
- نتیجه:
- GetDividendsForeign Issuer
- GetOperationsByCursor
- محاسبه و خروجی اطلاعات مورد علاقه
- کار با قیمت ها
- هزینه قراردادهای آتی
- بازار فرابورس
- عملیات ریاضی بر روی ابزار
- میکروسرویس آماده است!
- نتیجه گیری و برنامه ریزی برای آینده
- https://opexbot.info
چه چیزی مورد بحث قرار خواهد گرفت؟
- فقط بخش کاربردی در مورد توسعه.
- دانش و تجربه واقعی که در کار با ابزارهای مالی بسیار مهم است.
- مروری بر مسائلی که باید روی آنها کار کرد
بنابراین، من می خواهم آمار تجارت را محاسبه کنم و آن را به روشی راحت انجام دهم.
توسعه یک سرویس آمار گام به گام:
- اتصال به Tinkoff Invest API
- کشیدن داده ها از Tinkoff Invest API در یک مرورگر
- دریافت گزارش کارگزاری و معاملات
- محاسبه و خروجی اطلاعات مورد علاقه
- نتیجه گیری و برنامه ریزی برای آینده
اتصال به Tinkoff Invest API
برای اتصال به API، میتوانید هر sdk را از مستندات https://github.com/Tinkoff/investAPI#sdk بگیرید . یا بسته npm ` tinkoff-sdk-grpc-js` . مهم است که بسته توسط توسعه دهندگان به آخرین نسخه به روز شود. نصب
npm من tinkoff-sdk-grpc-js
چک کردن
const { createSdk } = require(‘tinkoff-sdk-grpc-js’); // توکنی که می توان به این شکل بدست آورد const TOKEN = ‘YOURAPI’; // نام برنامه ای که توسط آن می توانید در گزارش های TCS پیدا کنید. const appName = ‘tcsstat’; const sdk = createSdk(TOKEN, appName); (async () => { console.log(await sdk.users.getAccounts()); })();
نتیجه: لیستی از حساب های شما در کنسول نمایش داده می شود. به عنوان مثال، بیایید تفاوت های ظریف را تجزیه و تحلیل کنیم:
- در لیست حساب ها یک “بانک سرمایه گذاری” وجود دارد که نمی توانید با استفاده از API با آن کار کنید
- لطفاً توجه داشته باشید که فیلدها در camelCase هستند، در حالی که در مستندات این فیلدها به صورت under_score ارائه شده است.
- همه جا به این صورت خواهد بود، بنابراین نمی توانید فقط یک فیلد را از اسناد بردارید و کپی کنید.
مفید:
- این کد را می توانید در شاخه پروژه پیدا کنید
https://github.com/pskucherov/tcsstat/tree/step1 https://github.com/pskucherov/tcsstat/compare/step1
کشیدن داده ها از Tinkoff Invest API در یک مرورگر
من next.js و socket.io را گرفتم. این یک توصیه قوی نیست، به صلاحدید خود انتخاب کنید.
npx create-next-app@latest npm i socket.io socket.io-client
بلافاصله به مرحله دوستی next+socket+investapi می رویم و برای همه جزئیات، بخش مفید این مرحله را مشاهده می کنیم. من جزئیات را شرح می دهم:
- در سمت nodejs (سرور) یک فایل pages/api/investapi.js وجود دارد. اینجاست که ما سرور socket.io را ایجاد می کنیم و به investapi متصل می شویم.
- در سمت مرورگر (کلینت)، از طریق سوکت به سرور متصل می شویم و اطلاعات حساب را از کارگزار درخواست می کنیم.
- ما داده ها را از کارگزار روی سرور دریافت می کنیم، سپس آن را برای مشتری ارسال می کنیم. هنگامی که آنها در مشتری دریافت می شوند، در مرورگر نمایش داده می شوند.
نتیجه: در کنسول مرورگر میتوانیم اطلاعات مربوط به حسابها را ببینیم. یعنی در مرحله آخر اطلاعات اکانت ها را در کنسول سرور (nodejs) دیدیم، در مرحله فعلی این اطلاعات را به کلاینت (مرورگر) منتقل کردیم.
حالا بیایید آن را طوری انجام دهیم که بتوانید یک حساب کاربری از مرورگر انتخاب کنید و اگر توکن وجود نداشته باشد، خطا به کنسول ارسال می شود. کار ساده است و چیز جدیدی نیست، بنابراین من فقط به commit ها لینک می دهم
- https://github.com/pskucherov/tcsstat/commit/7e1ac57061e5e971588479015b06d8814d6609a9
- https://github.com/pskucherov/tcsstat/commit/b28ac973a57494f5232589b4cb6b9fb13b8af759
مفید:
- نحوه دوستیابی بعدی و سوکت در اینجا به تفصیل شرح داده شده است .
- کد دوستی next+socket+investapi:
https://github.com/pskucherov/tcsstat/commit/a443a4ac1bb4f0aa898f638128755fe7391ee381 برای کسانی که موارد فوق مشکل است، پس ما در این مرحله می مانیم و به کد می پردازیم. اگر سوالی دارید، بپرسید. https://github.com/pskucherov/tcsstat/tree/step2 https://github.com/pskucherov/tcsstat/compare/step1…step2
دریافت گزارش کارگزاری و معاملات
سه روش برای دریافت گزارش کارگزاری و معاملات وجود دارد
از همان ابتدا مهم است که بدانید:
- گزارش کارگزاری در حالت T-3 تولید می شود، یعنی. معاملات پس از اجرای واقعی در آنجا نمایش داده می شوند.
- بر این اساس در صورت درخواست این گزارش برای دو روز گذشته، سه روز دیگر آماده می شود.
- برای دریافت تراکنشهای روزهای آخر، از روش دریافت تراکنشها استفاده میکنیم، اما به یاد داشته باشید که شناسه و محتوای آنها ممکن است پس از ایجاد گزارش کارگزاری تغییر کند.
GetBrokerReport
برای دریافت گزارش کارگزاری، باید شناسه حساب، تاریخ شروع و تاریخ پایان گزارش را بگیرید، اما حداکثر 31 روز. ما درخواستی برای ایجاد گزارش به API در generate _broker_report_request ارسال می کنیم ، در پاسخ یک TaskId دریافت می کنیم. پس از آن، با استفاده از این taskId، داده هایی را از get _broker_report_response دریافت می کنیم.
- شما باید TaskID را برای همیشه دقیقاً برای این تاریخ ها ذخیره کنید.
- از آنجایی که اگر آن را گم کنید، برای تاریخ های درخواستی، ابتدا گزارش در پاسخ به درخواست تولید می آید،
- و بعد اصلاً نمی آید.
[/اسپویلر] بیایید شروع به نوشتن کد کنیم
روش بدست آوردن تاریخ با در نظر گرفتن تفریق از تاریخ فعلی
const getDateSubDay = (subDay = 5, start = true) => { const date = new Date(); date.setUTCDate(date.getUTCDate() – subDay); if (شروع) { date.setUTCHours(0, 0, 0, 0); } else { date.setUTCHours(23, 59, 59, 999); } تاریخ بازگشت؛ };
درخواست تولید گزارش
const brokerReport = await (sdk.operations.getBrokerReport)({ geneBrokerReportRequest: { accountId, from, to, }, });
نتیجه:
- در نتیجه اولین اجرای دستور، taskId را دریافت می کنیم.
- گزارش از طرف کارگزار شروع به تولید می کند. وقتی آماده است ناشناخته است، منتظر می مانیم و به صورت دوره ای taskId را در انتظار گزارش می کشیم.
- چرا؟ چون اگر گزارش آماده نباشد خطا می دهد. اگر گزارش از طرف کارگزار آماده نیست، این یک خطا در کد شما است. لطفاً پردازش کنید: 30058|INVALID_ARGUMENT|کار هنوز تکمیل نشده است، لطفاً بعداً دوباره امتحان کنید
کد انتظار و دریافت گزارش چیزی شبیه به این است.
const timer = async time => { return new Promise(resolve => setTimeout(resolve, time)); } const getBrokerResponseByTaskId = async (taskId, page = 0) => { try { return await (sdk.operations.getBrokerReport)({ getBrokerReportRequest: { taskId, page, }, }); } catch (e) { console.log(‘wait’, e); تایمر انتظار (10000); بازگشت در انتظار getBrokerResponseByTaskId(taskId, page); } }
سپس همان جادو اتفاق می افتد. ما اسکریپت خود را متوقف می کنیم، دوباره آن را شروع می کنیم، taskId نداریم. ما کد را با درخواست taskId اجرا می کنیم، اما دیگر taskId را دریافت نمی کنیم، بلکه بلافاصله گزارش را دریافت می کنیم. شعبده بازي! و اگر همیشه همینطور بود همه چیز خوب بود. اما یک ماه دیگر هیچ داده ای وجود نخواهد داشت. مفید :
- کمی تئوری در اینجا و اینجا بیان شده است .
- با کنار هم قرار دادن کد، پیش نویس چیزی شبیه به این خواهد بود.
https://github.com/pskucherov/tcsstat/tree/step3.1 https://github.com/pskucherov/tcsstat/compare/step3.1
- اگر کسی به این موضوع برخورد کرد، به این موضوع خوش آمدید . پس از تعمیر این جادو، قدرت خود را از دست می دهد و به نوعی متفاوت می شود. اما در لحظه فعلی (2023/03/21) دقیقاً به همین ترتیب کار می کند.
GetDividendsForeign Issuer
ممکن است کسی فکر کند که روش مشابه روش قبلی است و شما می توانید از یک روش استفاده کنید که در آن فقط نام عملیات را تغییر دهید. اما آنها حدس نمی زدند! نام گذاری در آنجا هم در روش ها و هم در اطلاعات برگشتی بسیار متفاوت است. و تعداد صفحات از 0 و سپس از 1 شروع می شود. برای اینکه در همه اینها گیج نشوید، نوشتن دو روش مختلف آسان تر است. که عجیب است، زیرا منطق کار هم همینه زمانی که سعی کردم یک روش بسازم و کد کمتری وجود داشت، مدت زیادی تف کردم. در اینجا هیچ نمونه ای وجود نخواهد داشت.
GetOperationsByCursor
مورد علاقه من از این سه. اگرچه دقیق ترین نیست، اما کافی ترین. ما از ابتدای ایجاد حساب کاربری تا حداکثر تاریخ ممکن (بستن حساب یا فعلی) درخواست می دهیم. ما پاسخ را دریافت می کنیم، مکان نما را می گیریم و تا زمانی که داده وجود دارد، دوباره درخواست می کنیم. و کد مختصرتر از مثال های بالا است.
const timer = async time => { return new Promise(resolve => setTimeout(resolve, time)); } const getOperationsByCursor = async (sdk, accountId, from, to, cursor =”) => { try { const reqData = { accountId, from, to, limit: 1000, state: sdk.OperationState.OPERATION_STATE_EXECUTED, بدون Commissions: false: withoutTrades: false, withoutOvernights: false, cursor, }; بازگشت در انتظار sdk.operations.getOperationsByCursor(reqData); } catch (e) { await timer(60000); بازگشت در انتظار getOperationsByCursor(sdk, accountId, from, to, cursor = ”); } }
پیش نویس برای اجرا در اینجا است: https://github.com/pskucherov/tcsstat/tree/step3.3 https://github.com/pskucherov/tcsstat/compare/step3.3 اکنون ما آماده هستیم تا عملیات دریافت را به برنامه ما اگر به درستی انجام شود، باید گزارش کارگزاری را برای کل وجود حساب دریافت کنید. و برای داده های از دست رفته، همان T-3 ها، دوباره از عملیات بارگیری می شوند. اما این را می توان در یک مقاله جداگانه جدا کرد. از تفاوت های اصلی که با آن روبرو خواهید شد چسباندن عملیات و گزارش کارگزاری است.
- اگر امروز یک گزارش کارگزاری و معاملات برای تاریخ های مورد نیاز دریافت کردید، همه آن را در پایگاه داده قرار دهید، پس مشکلی وجود ندارد.
- فردا زمانی که بخش بعدی داده ها را از گزارش و عملیات دریافت کردید و تصمیم گرفتید آنها را با پایگاه داده موجود همگام سازی کنید، با مشکل مواجه خواهید شد.
- تفاوت های ظریف زیادی در مورد عدم تطابق یا تغییر شناسه پس از پردازش
- سپس برای بازار فرابورس، شناسه ها اصلا مطابقت ندارند.
- و همچنین تفاوت های ظریف ابزارهای همگام سازی، که به دلیل ویژگی های API، دوباره با هم مطابقت ندارند. ولی اون یک ماجرای دیگه است.
بیایید دریافت اطلاعات در مورد عملیات را به برنامه خود اضافه کنیم. سوال اصلی این است که داده ها در کجا پردازش و ذخیره می شوند.
- اگر این کار را برای خودتان انجام دهید، داده های مشابهی را از دستگاه های مختلف مصرف خواهید کرد. سپس باید داده ها را روی سرور پردازش و ذخیره کنید.
- اگر دادههای مختلف زیادی دارید که توسط کاربران مختلف مصرف میشود، پس باید تصمیم بگیرید که چه چیزی مهمتر است: سرعت کاربران یا صرفهجویی در مصرف آهن. هر کسی که توانایی خرید بینهایت سختافزار را دارد، همه چیز را روی سرورش حساب میکند و آن را برای کاربران بسیار سریع میکند و باعث صرفهجویی در منابع کاربر، مانند باتری و ترافیک میشود که در گوشیها بسیار مهم است.
به نوبه خود، شمارش در مرورگر در اصل بهینه ترین راه حل نیست. بنابراین، آنچه گران نیست، آن را در سرور خود در نظر می گیریم. بقیه را به مشتری واگذار می کنیم. من واقعاً می خواهم کمیسیون سرور را بگیرم و محاسبه کنم. اما در اینجا تفاوت ظریفی به نام “تعامل” مطرح می شود. فرض کنید شما هزاران عملیات دارید و دریافت آنها پنج دقیقه طول می کشد. کاربر در این زمان چه چیزی خواهد داشت؟ اسپینر؟ پیش رفتن؟ اینفا در مورد چقدر آپلود شد؟ زمانی که کاربر در فرآیند می تواند چیزی را ببیند، استفاده از “انتظار فعال” ایده آل است. در اینجا نتیجه است :
- در حال بارگذاری صفحه
- کلیه فاکتورها درخواستی می باشد
- پس از آن کلیه تراکنش ها با کمیسیون تراکنش های انجام شده برای تمامی حساب ها درخواست می شود. با دریافت داده ها، در مرورگر ارائه می شود.
برای اینکه هر بار دادههای رویدادها را فیلتر نکنیم، رویداد خود را برای هر حساب میکشیم. مثل این:
socket.emit(‘sdk:getOperationsCommissionResult_’ + accountId, { items: data?.items, inProgress: Boolean(nextCursor), });
پیشنویس راهاندازی اینجاست: https://github.com/pskucherov/tcsstat/tree/step3 https://github.com/pskucherov/tcsstat/compare/step2…step3 در حال حرکت. خیلی خوبه که این خط رو خوندی!
محاسبه و خروجی اطلاعات مورد علاقه
بستگی به این دارد که چه کسی به چه اطلاعاتی نیاز دارد. بنابراین ، من فوراً تفاوت های ظریف اصلی را که با آنها روبرو خواهید شد به شما می گویم.
کار با قیمت ها
همه کسانی که با امور مالی کار می کنند می دانند که معاملات پولی فقط باید با اعداد کامل انجام شود. به دلیل عدم دقت مقادیر بعد از اعشار و خطای تجمعی با تعداد زیادی عملیات. به همین دلیل است که تمام قیمت ها در قالب MoneyValue زیر ارائه می شوند
رشته | نوع | شرح |
---|---|---|
واحد پول | رشته | رشته کد ارز ISO |
واحدها | int64 | قسمت صحیح جمع، می تواند یک عدد منفی باشد |
نانو | int32 | قسمت کسری مقدار، می تواند یک عدد منفی باشد |
ما آنها را جداگانه پردازش می کنیم، سپس آنها را به ارزش قیمت می آوریم:
quotation.unit + quotation.nano / 1e9
هزینه قراردادهای آتی
قیمت آتی به صورت امتیازی ارائه می شود، زمانی که شما آتی ارز دارید، باید نرخ را بدانید. و البته قیمت در امتیاز و مرحله قیمت. هنگامی که شما سود حاصل از معاملات را محاسبه می کنید، این می تواند شلیک کند، زیرا. اگر مبلغ کل را با ضرب قیمت در مقدار محاسبه کنید. در اینجا شما باید مراقب باشید. فعلاً، خواهیم دید که چگونه پیش خواهد رفت. این در مورد معاملات آتی ارز صدق می کند، در جاهای دیگر همه چیز با این اوکی است.
بازار فرابورس
این بازار ویژگی های زیادی دارد، بنابراین بیایید عملیات روی آن را جداگانه مطالعه کنیم. هنگامی که عملیات همگام سازی را شروع می کنید، معلوم می شود که برای مطابقت صحیح با ابزار، باید فیگی / تیکر را به همان شکل بیاورید. وقتی شروع به همگام سازی با گزارش کارگزاری می کنید، مشخص می شود که شناسه تجاری همان تراکنش در ابتدای معاملات دارای حروف است و در گزارش کارگزاری وجود ندارد. بنابراین، آنها را نمی توان مقایسه کرد … ahem-ahem … با مقایسه! من زمان معامله، تیک و تطبیق آن که یک tradeId در دیگری موجود است را مطابقت دادم. درست است، من نمی دانم. هر کسی که با این موضوع روبرو می شود و به آن اهمیت می دهد، به این موضوع بیاید یا موضوع جدیدی را شروع کند.
عملیات ریاضی بر روی ابزار
انجام عملیات ریاضی با کل لیست غیرممکن است، بدون نگاه کردن. برای اینکه گرم به سافت اضافه نشود، همیشه ارز را چک می کنیم و فقط در صورتی که مطمئن باشیم ارز مطابقت دارد و امتیازها به ارز مورد نظر تبدیل شده اند، پردازش می کنیم. با داشتن دانش در مورد کار با شماره های بانکی، کمیسیون هزینه شده برای هر یک از حساب ها را محاسبه می کنیم. مانند این: https://github.com/pskucherov/tcsstat/tree/step4 https://github.com/pskucherov/tcsstat/compare/step3…step4
میکروسرویس آماده است!
https://github.com/pskucherov/tcsstat به عنوان یک تکلیف، می توانید بررسی کنید که آیا سرویس با اتصال کند کار می کند، زمانی که اتصالات قطع می شود، زمانی که اینترنت قطع می شود، زمانی که خطاها یا محدودیت های منقضی شده از طرف کارگزار وجود دارد یا خیر.
نتیجه گیری و برنامه ریزی برای آینده
- در مورد عملیات اساسی و کار با Invest API یاد گرفت
- زمان صرف شده ~ 10 ساعت
- سطح دشواری ~ جونیور+ / متوسط پایین
اگر به اصلاح میکروسرویس ادامه دهید، ممکن است با چیزی شبیه به این مواجه شوید
https://opexbot.info
این پیشرفت من است، برای کسانی که بیش از حد تنبل هستند که نمی توانند بفهمند، اجرا کنند و روی خودشان حساب کنند. من قصد دارم به درخواست کاربران تجزیه و تحلیل را در آنجا اضافه کنم. اگر مقاله را دوست داشتید، در کانال تلگرام من عضو شوید .
Полезная статья. Не могу представить, сколько усилий автора потребовалось, чтобы все описать. Благодарю.