├── .gitignore
├── README.md
├── screenshots
└── Mainscreen.png
└── src
├── 3Commas.BotCreator.Tests
├── 3Commas.BotCreator.Tests.csproj
├── AutomapperTests.cs
├── BinanceTests.cs
├── BotManagerTests.cs
└── NameHelperTests.cs
├── 3Commas.BotCreator.sln
└── 3Commas.BotCreator
├── 3Commas.BotCreator.csproj
├── 3CommasLayer
├── IXCommasClient.cs
└── Implementations
│ └── XCommasClient.cs
├── App.config
├── ExchangeLayer
├── Entities
│ ├── Pair.cs
│ └── PlaceOrderResult.cs
├── IExchange.cs
└── Implementations
│ ├── Binance.cs
│ └── Huobi.cs
├── Infrastructure
├── IViewBase.cs
└── Presenter.cs
├── Keys.cs
├── MappingProfiles
└── BotSettingsProfile.cs
├── Misc
├── AssemblyHelper.cs
├── BotManager.cs
├── ControlHelper.cs
├── NameHelper.cs
└── TextBoxLogger.cs
├── Program.cs
├── Properties
├── AssemblyInfo.cs
├── DataSources
│ └── _3Commas.BotCreator.Views.MainForm.BotSettings.datasource
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── Resources
├── 3Commas.png
├── Add_16x16.png
├── Binance.png
├── Cancel_16x16.png
├── Clear_16x16.png
├── Close_16x16.png
├── Huobi.png
├── Info_16x16.png
├── Info_32x32.png
├── Play_16x16.png
├── Properties_32x32.png
├── Save_16x16.png
├── ShowTestReport_16x16.png
├── Suggestion_16x16.png
└── download.png
├── Services
├── BotSettingService
│ ├── BotSettingService.cs
│ ├── BotSettings.cs
│ └── IBotSettingService.cs
└── MessageBoxService
│ ├── IMessageBoxService.cs
│ └── MessageBoxService.cs
├── Views
├── AboutBox
│ ├── AboutBox.Designer.cs
│ ├── AboutBox.cs
│ └── AboutBox.resx
├── ChooseSignalView
│ ├── ChooseSignalView.Designer.cs
│ ├── ChooseSignalView.cs
│ └── ChooseSignalView.resx
├── MainForm
│ ├── BotSettingViewModel.cs
│ ├── IMainForm.cs
│ ├── MainForm.Designer.cs
│ ├── MainForm.cs
│ ├── MainForm.resx
│ └── MainFormPresenter.cs
├── SaveTemplateView
│ ├── SaveTemplateView.Designer.cs
│ ├── SaveTemplateView.cs
│ └── SaveTemplateView.resx
└── SetApiKeyView
│ ├── SetApiKeyView.Designer.cs
│ ├── SetApiKeyView.cs
│ └── SetApiKeyView.resx
├── packages.config
└── robot.ico
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 3Commas.BotCreator
2 |
3 | This unofficial tool makes it possible to create several simple DCA bots at once.
4 |
5 | **Note that only this use case is covered:**
6 |
7 | I needed something that would allow me to create multiple simple DCA bots with the same quote currency. It will automatically find the next base currency ordered by Volume descending.
8 | If short bots are created, the tool can buy base coins for you directly on your exchange (if desired).
9 |
10 |
11 |
12 | ---
13 |
14 | ## Technical description
15 |
16 | The implementation is based on .Net Framework, since the View Designer was not yet productive for NET5 at this point in time.
17 |
18 | Therefore I created the tool more or less quick & dirty because I needed this functionality quickly. Time was money in this case :)
19 |
20 | Implementation is build upon the CryptoExchange.Net, 3Commas.Net, Binance.Net and Huobi.Net libraries. Thanks for the brilliant work!
21 |
22 | The only use cases available are the ones I needed for myself. But if you need something, [let me know](https://github.com/MarcDrexler/3Commas.BotCreator/issues).
23 |
24 | Also if you think something is broken or have any questions, please open an [Issue](https://github.com/MarcDrexler/3Commas.BotCreator/issues).
25 |
26 | ## Features
27 |
28 | - Add as many bots as you want
29 | - Uses pairs at the highest volume first
30 | - Skip bot creation if base is stablecoin
31 | - Skip bot creation for blacklisted pairs
32 | - Skip bot creation for existing pair + strategy
33 | - This way you can create 10 Bots, and another day the next 10 Bots without having to worry about duplicates.
34 | - Specify your own name schema. There are existing placeholders for Strategy and Pair.
35 | - Templates
36 | - Signals currently available: Manual, Nonstop, TradingView, RSI, ULT, TA Presets, Custom
37 | - Buys tokens for your new Short bots via market buy order (Binance & Huobi only)
38 | - Click the Preview button to see what happens when you hit the Create button :)
39 |
40 | ### Excluded Pairs
41 |
42 | All pairs that end with UP, DOWN, BEAR and BULL are skipped.
43 |
44 | These are currently hard coded.
45 |
46 | ## Screenshots
47 |
48 | 
49 |
50 | ## Prerequisites
51 |
52 | - .NET Framework 4.7.2 (which already might be installed on your Windows machine)
53 | - 3Commas API key and secret
54 | - Exchange API key and secret (Binance or Huobi)
55 |
56 | **Note:** By default, API keys are not stored on your computer and must be entered again the next time the application is started.
57 | There is an option to persist ApiKey and Secret. However, use is at your own risk. Other programs could read this information.
58 |
59 | ### Why API Keys for my Exchange?
60 |
61 | The app will find suitable pairs for you. It simply queries your exchange for pairs and ranks them in descending order of volume.
62 | If you want to create short bots and don't have tokens for the new pairs, tokens are automatically bought for you (if you choose to).
63 |
64 | ## Installer
65 |
66 | I use MS ClickOnce for installation and updates.
67 |
68 | An installer with the current version is hosted [here](https://marcdrexler.blob.core.windows.net/botcreator/BotCreator.application)
69 |
70 | Updates are automatically checked for each start.
71 |
72 | Because the package is not signed with a public certificate, you might see some security warnings when installing and starting the app if you are using Windows 7 or later.
73 |
74 | ## Support
75 |
76 | I develop and maintain this package on my own for free in my spare time.
77 | Donations are greatly appreciated and a motivation to keep improving.
78 |
79 | XMR: 89rmrxDAGAWWhSZXhnNf335qYfyXz4vQsNAM1VFSg6Y7Tve9BGF9pwte9ps61E9FY76r4onhWw7e19eu7fM8BARQMRHNBt7
80 |
81 | or
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/screenshots/Mainscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/screenshots/Mainscreen.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator.Tests/3Commas.BotCreator.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472
5 | _3Commas.BotCreator.Tests
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator.Tests/AutomapperTests.cs:
--------------------------------------------------------------------------------
1 | using _3Commas.BotCreator.MappingProfiles;
2 | using AutoMapper;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace _3Commas.BotCreator.Tests
6 | {
7 | [TestClass]
8 | public class AutomapperTests
9 | {
10 | [TestMethod]
11 | public void AutoMapper_Configuration_IsValid()
12 | {
13 | var config = new MapperConfiguration(cfg => cfg.AddProfile());
14 | config.AssertConfigurationIsValid();
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator.Tests/BinanceTests.cs:
--------------------------------------------------------------------------------
1 | using _3Commas.BotCreator.ExchangeLayer.Implementations;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace _3Commas.BotCreator.Tests
5 | {
6 | [TestClass]
7 | public class BinanceTests
8 | {
9 | [TestMethod]
10 | public void ExtractBaseCurrencyTest()
11 | {
12 | var actual = new Binance(FakeKeys).ExtractBaseCurrency("usdtbnb", "usdt");
13 |
14 | Assert.AreEqual("BNB", actual);
15 | }
16 |
17 | [TestMethod]
18 | public void ToBinanceSymbolTest()
19 | {
20 | var actual = new Binance(FakeKeys).ToBinanceSymbol("usdt", "bnb");
21 |
22 | Assert.AreEqual("BNBUSDT", actual);
23 | }
24 |
25 | public Keys FakeKeys => new Keys
26 | {
27 | ApiKey3Commas = "X",
28 | ApiKeyBinance = "X",
29 | Secret3Commas = "X",
30 | SecretBinance = "X"
31 | };
32 | }
33 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator.Tests/BotManagerTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using _3Commas.BotCreator._3CommasLayer;
5 | using _3Commas.BotCreator.ExchangeLayer;
6 | using _3Commas.BotCreator.ExchangeLayer.Entities;
7 | using _3Commas.BotCreator.Misc;
8 | using _3Commas.BotCreator.Views.MainForm;
9 | using Microsoft.Extensions.Logging.Abstractions;
10 | using Microsoft.VisualStudio.TestTools.UnitTesting;
11 | using Moq;
12 | using XCommas.Net;
13 | using XCommas.Net.Objects;
14 |
15 | namespace _3Commas.BotCreator.Tests
16 | {
17 | [TestClass]
18 | public class BotManagerTests
19 | {
20 | [TestMethod]
21 | public async Task CreateBots_Create5Bots_5BotsCreated()
22 | {
23 | // Arrange
24 | var exchange = BuildExchangeWith5Pairs();
25 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
26 | var request = GetDefaultRequest();
27 |
28 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
29 |
30 | // Act
31 | await target.CreateBots(5, false, request, CancellationToken.None);
32 |
33 | // Assert
34 | xCommasClient.Verify(x => x.CreateBotAsync(It.IsAny(), request.Strategy, It.IsAny()), Times.Exactly(5));
35 | }
36 |
37 | [TestMethod]
38 | public async Task CreateBots_CreateMoreBotsThanPairsAvailable_5BotsCreated()
39 | {
40 | // Arrange
41 | var exchange = BuildExchangeWith5Pairs();
42 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
43 | var request = GetDefaultRequest();
44 |
45 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
46 |
47 | // Act
48 | await target.CreateBots(10, false, request, CancellationToken.None);
49 |
50 | // Assert
51 | xCommasClient.Verify(x => x.CreateBotAsync(It.IsAny(), request.Strategy, It.IsAny()), Times.Exactly(5));
52 | }
53 |
54 | [TestMethod]
55 | public async Task CreateBots_Disable_WillNotEnableBots()
56 | {
57 | // Arrange
58 | var exchange = BuildExchangeWith5Pairs();
59 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
60 | var request = GetDefaultRequest();
61 |
62 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
63 |
64 | // Act
65 | await target.CreateBots(5, false, request, CancellationToken.None);
66 |
67 | // Assert
68 | xCommasClient.Verify(x => x.EnableBotAsync(It.IsAny()), Times.Never);
69 | }
70 |
71 | [TestMethod]
72 | public async Task CreateBots_Enable_WillEnableBots()
73 | {
74 | // Arrange
75 | var exchange = BuildExchangeWith5Pairs();
76 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
77 | var request = GetDefaultRequest();
78 |
79 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
80 |
81 | // Act
82 | await target.CreateBots(5, true, request, CancellationToken.None);
83 |
84 | // Assert
85 | xCommasClient.Verify(x => x.EnableBotAsync(It.IsAny()), Times.Exactly(5));
86 | }
87 |
88 | [TestMethod]
89 | public async Task CreateBots_CheckForExistingBots_4BotsCreated()
90 | {
91 | // Arrange
92 | var exchange = BuildExchangeWith5Pairs();
93 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
94 | var request = GetDefaultRequest();
95 | request.SkipExistingPairs = true;
96 |
97 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
98 |
99 | // Act
100 | await target.CreateBots(5, false, request, CancellationToken.None);
101 |
102 | // Assert
103 | xCommasClient.Verify(x => x.CreateBotAsync(It.IsAny(), request.Strategy, It.IsAny()), Times.Exactly(4));
104 | }
105 |
106 | [TestMethod]
107 | public async Task CreateBots_CheckForBlacklistedPairs_4BotsCreated()
108 | {
109 | // Arrange
110 | var exchange = BuildExchangeWith5Pairs();
111 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
112 | var request = GetDefaultRequest();
113 | request.SkipBlacklistedPairs = true;
114 |
115 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
116 |
117 | // Act
118 | await target.CreateBots(5, false, request, CancellationToken.None);
119 |
120 | // Assert
121 | xCommasClient.Verify(x => x.CreateBotAsync(It.IsAny(), request.Strategy, It.IsAny()), Times.Exactly(4));
122 | }
123 |
124 | [TestMethod]
125 | public async Task CreateBots_CheckForBaseStablecoins_4BotsCreated()
126 | {
127 | // Arrange
128 | var exchange = BuildExchangeWith5Pairs();
129 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
130 | var request = GetDefaultRequest();
131 | request.SkipBaseStablecoin = true;
132 |
133 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
134 |
135 | // Act
136 | await target.CreateBots(5, false, request, CancellationToken.None);
137 |
138 | // Assert
139 | xCommasClient.Verify(x => x.CreateBotAsync(It.IsAny(), request.Strategy, It.IsAny()), Times.Exactly(4));
140 | }
141 |
142 | [TestMethod]
143 | public async Task CreateBots_CheckForExistingBotsAndBlacklistedPairsAndBaseStablecoins_2BotsCreated()
144 | {
145 | // Arrange
146 | var exchange = BuildExchangeWith5Pairs();
147 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
148 | var request = GetDefaultRequest();
149 | request.SkipExistingPairs = true;
150 | request.SkipBlacklistedPairs = true;
151 | request.SkipBaseStablecoin = true;
152 |
153 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
154 |
155 | // Act
156 | await target.CreateBots(5, false, request, CancellationToken.None);
157 |
158 | // Assert
159 | xCommasClient.Verify(x => x.CreateBotAsync(It.IsAny(), request.Strategy, It.IsAny()), Times.Exactly(2));
160 | }
161 |
162 | [TestMethod]
163 | public async Task CreateBots_NoBuyOrderWanted_WillNotPlaceBuyOrder()
164 | {
165 | // Arrange
166 | var exchange = BuildExchangeWith5Pairs();
167 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
168 | var request = GetDefaultRequest();
169 |
170 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
171 |
172 | // Act
173 | await target.CreateBots(5, false, request, CancellationToken.None);
174 |
175 | // Assert
176 | exchange.Verify(x => x.PlaceOrder(It.IsAny(), It.IsAny()), Times.Never);
177 | }
178 |
179 | [TestMethod]
180 | public async Task CreateBots_BuyOrderWanted_WillPlaceBuyOrder()
181 | {
182 | // Arrange
183 | var exchange = BuildExchangeWith5Pairs();
184 | var xCommasClient = BuildXCommasClientWith1ExistingAnd1Blacklisted();
185 | var request = GetDefaultRequest();
186 | request.BuyBaseCurrency = true;
187 | request.BaseCurrencyToBuy = (decimal) 123.45;
188 |
189 | var target = new BotManager(NullLogger.Instance, xCommasClient.Object, exchange.Object);
190 |
191 | // Act
192 | await target.CreateBots(5, false, request, CancellationToken.None);
193 |
194 | // Assert
195 | exchange.Verify(x => x.PlaceOrder(It.IsAny(), (decimal) 123.45), Times.Exactly(5));
196 | }
197 |
198 | [TestMethod]
199 | public void TransformPairTo3CommasSymbolStringTest()
200 | {
201 | var actual = BotManager.TransformPairTo3CommasSymbolString("USDT", "BNB");
202 |
203 | Assert.AreEqual("USDT_BNB", actual);
204 | }
205 |
206 | private Mock BuildXCommasClientWith1ExistingAnd1Blacklisted()
207 | {
208 | var mock = new Mock();
209 | mock.Setup(x => x.GetBotsAsync(It.IsAny(), 0, Strategy.Long, BotScope.Enabled)).ReturnsAsync(new XCommasResponse(new[]
210 | {
211 | new Bot() {Pairs = new[] {"usdt_btc"}, Strategy = Strategy.Long},
212 | }, "", ""));
213 | mock.Setup(x => x.GetBotsAsync(It.IsAny(), 100, Strategy.Long, BotScope.Enabled)).ReturnsAsync(new XCommasResponse(new Bot[0], "", ""));
214 | mock.Setup(x => x.GetBotsAsync(It.IsAny(), It.IsAny(), Strategy.Long, BotScope.Disabled)).ReturnsAsync(new XCommasResponse(new Bot[0], "", ""));
215 | mock.Setup(x => x.GetBotsAsync(It.IsAny(), It.IsAny(), Strategy.Short, BotScope.Enabled)).ReturnsAsync(new XCommasResponse(new Bot[0], "", ""));
216 | mock.Setup(x => x.GetBotsAsync(It.IsAny(), It.IsAny(), Strategy.Short, BotScope.Disabled)).ReturnsAsync(new XCommasResponse(new Bot[0], "", ""));
217 |
218 | mock.Setup(x => x.GetBotPairsBlackListAsync()).ReturnsAsync(new XCommasResponse(new BotPairsBlackListData() {Pairs = new[] {"USDT_ETH"}}, "", ""));
219 |
220 | mock.Setup(x => x.CreateBotAsync(1000, It.IsAny(), It.IsAny())).ReturnsAsync(new XCommasResponse(new Bot(), "", ""));
221 |
222 | mock.Setup(x => x.EnableBotAsync(It.IsAny())).ReturnsAsync(new XCommasResponse(new Bot(), "", ""));
223 |
224 | mock.Setup(x => x.GetCurrencyRateAsync(It.IsAny())).ReturnsAsync(new XCommasResponse(new CurrencyRate(), "", ""));
225 |
226 | return mock;
227 | }
228 |
229 | private Mock BuildExchangeWith5Pairs()
230 | {
231 | var exchange = new Mock();
232 | exchange.Setup(x => x.GetAllPairsByQuoteCurrency("USDT")).ReturnsAsync(new List
233 | {
234 | new Pair {BaseCurrency = "BTC", QuoteCurrency = "USDT", TotalTradedQuoteAssetVolume = 10000000},
235 | new Pair {BaseCurrency = "ETH", QuoteCurrency = "USDT", TotalTradedQuoteAssetVolume = 100000},
236 | new Pair {BaseCurrency = "LTC", QuoteCurrency = "USDT", TotalTradedQuoteAssetVolume = 10000},
237 | new Pair {BaseCurrency = "LINK", QuoteCurrency = "USDT", TotalTradedQuoteAssetVolume = 100},
238 | new Pair {BaseCurrency = "BUSD", QuoteCurrency = "USDT", TotalTradedQuoteAssetVolume = 10},
239 | });
240 |
241 | exchange.Setup(x => x.PlaceOrder(It.IsAny(), (decimal) 123.45)).ReturnsAsync(new PlaceOrderResult() {Success = true});
242 |
243 | return exchange;
244 | }
245 |
246 | private BotSettingViewModel GetDefaultRequest() => new BotSettingViewModel(LeverageType.NotSpecified, 0, (decimal)0, StopLossType.StopLoss, false, 0, false, false, false, "USDT", Strategy.Long, StartOrderType.Limit, 5, 1, 1, 1, 1, (decimal)1.5, false, 0, "{strategy} {pair} Bot", 10, 11, new List(), 0, 1000, 0);
247 | }
248 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator.Tests/NameHelperTests.cs:
--------------------------------------------------------------------------------
1 | using _3Commas.BotCreator.Misc;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using XCommas.Net.Objects;
4 |
5 | namespace _3Commas.BotCreator.Tests
6 | {
7 | [TestClass]
8 | public class NameHelperTests
9 | {
10 | [TestMethod]
11 | public void GenerateBotNameTest()
12 | {
13 | var actual = NameHelper.GenerateBotName("My new {pair} {strategy} bot", "USDT_BNB", Strategy.Long);
14 |
15 | Assert.AreEqual("My new USDT_BNB Long bot", actual);
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30406.217
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "3Commas.BotCreator", "3Commas.BotCreator\3Commas.BotCreator.csproj", "{02D16940-5926-45EA-A706-21B8D3E1F643}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "3Commas.BotCreator.Tests", "3Commas.BotCreator.Tests\3Commas.BotCreator.Tests.csproj", "{AA38FEBA-8B7D-4E96-934D-B4022E69027F}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {02D16940-5926-45EA-A706-21B8D3E1F643}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {02D16940-5926-45EA-A706-21B8D3E1F643}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {02D16940-5926-45EA-A706-21B8D3E1F643}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {02D16940-5926-45EA-A706-21B8D3E1F643}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {AA38FEBA-8B7D-4E96-934D-B4022E69027F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {AA38FEBA-8B7D-4E96-934D-B4022E69027F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {AA38FEBA-8B7D-4E96-934D-B4022E69027F}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {AA38FEBA-8B7D-4E96-934D-B4022E69027F}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {62584358-3810-4548-BCD9-DC2AB25F5DDC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/3CommasLayer/IXCommasClient.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using XCommas.Net;
3 | using XCommas.Net.Objects;
4 |
5 | namespace _3Commas.BotCreator._3CommasLayer
6 | {
7 | public interface IXCommasClient
8 | {
9 | Task> GetBotPairsBlackListAsync();
10 |
11 | Task> EnableBotAsync(int botId);
12 |
13 | Task> CreateBotAsync(int accountId, Strategy strategy, BotData botData);
14 |
15 | Task> UpdateBotAsync(int botId, BotUpdateData botUpdateData);
16 |
17 | Task> GetBotsAsync(int limit, int offset);
18 |
19 | Task> GetAccountsAsync();
20 |
21 | Task> GetCurrencyRateAsync(string symbol);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/3CommasLayer/Implementations/XCommasClient.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using XCommas.Net;
3 | using XCommas.Net.Objects;
4 |
5 | namespace _3Commas.BotCreator._3CommasLayer.Implementations
6 | {
7 | public class XCommasClient : IXCommasClient
8 | {
9 | private readonly XCommasApi _3CommasClient;
10 |
11 | public XCommasClient(Keys settings, bool usePaperTrading)
12 | {
13 | _3CommasClient = new XCommasApi(settings.ApiKey3Commas, settings.Secret3Commas, userMode: usePaperTrading ? UserMode.Paper : UserMode.Real);
14 | }
15 |
16 | public async Task> CreateBotAsync(int accountId, Strategy strategy, BotData botData)
17 | {
18 | return await _3CommasClient.CreateBotAsync(accountId, strategy, botData);
19 | }
20 |
21 | public async Task> UpdateBotAsync(int botId, BotUpdateData botUpdateData)
22 | {
23 | return await _3CommasClient.UpdateBotAsync(botId, botUpdateData);
24 | }
25 |
26 | public async Task> GetBotsAsync(int limit, int offset)
27 | {
28 | return await _3CommasClient.GetBotsAsync(limit, offset);
29 | }
30 |
31 | public async Task> GetAccountsAsync()
32 | {
33 | return await _3CommasClient.GetAccountsAsync();
34 | }
35 |
36 | public async Task> GetCurrencyRateAsync(string pair)
37 | {
38 | return await _3CommasClient.GetCurrencyRateAsync(pair);
39 | }
40 |
41 | public async Task> GetBotPairsBlackListAsync()
42 | {
43 | return await _3CommasClient.GetBotPairsBlackListAsync();
44 | }
45 |
46 | public async Task> EnableBotAsync(int botId)
47 | {
48 | return await _3CommasClient.EnableBotAsync(botId);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | <_3Commas.BotCreator.Properties.Settings>
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/ExchangeLayer/Entities/Pair.cs:
--------------------------------------------------------------------------------
1 | namespace _3Commas.BotCreator.ExchangeLayer.Entities
2 | {
3 | public class Pair
4 | {
5 | public decimal TotalTradedQuoteAssetVolume { get; set; }
6 | public string QuoteCurrency { get; set; }
7 | public string BaseCurrency { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/ExchangeLayer/Entities/PlaceOrderResult.cs:
--------------------------------------------------------------------------------
1 | namespace _3Commas.BotCreator.ExchangeLayer.Entities
2 | {
3 | public class PlaceOrderResult
4 | {
5 | public bool Success { get; set; }
6 | public string Message { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/ExchangeLayer/IExchange.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using _3Commas.BotCreator.ExchangeLayer.Entities;
4 |
5 | namespace _3Commas.BotCreator.ExchangeLayer
6 | {
7 | public interface IExchange
8 | {
9 | Task> GetAllPairsByQuoteCurrency(string quoteCurrency);
10 | Task PlaceOrder(Pair pair, decimal amountToBuyInQuoteCurrency);
11 | string Name { get; }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/ExchangeLayer/Implementations/Binance.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using _3Commas.BotCreator.ExchangeLayer.Entities;
5 | using Binance.Net;
6 | using Binance.Net.Enums;
7 | using Binance.Net.Objects.Spot;
8 | using CryptoExchange.Net.Authentication;
9 |
10 | namespace _3Commas.BotCreator.ExchangeLayer.Implementations
11 | {
12 | public class Binance : IExchange
13 | {
14 | public Binance(Keys settings)
15 | {
16 | BinanceClient.SetDefaultOptions(new BinanceClientOptions { ApiCredentials = new ApiCredentials(settings.ApiKeyBinance, settings.SecretBinance) });
17 | }
18 |
19 | public string Name => "Binance";
20 |
21 | public async Task> GetAllPairsByQuoteCurrency(string quoteCurrency)
22 | {
23 | using (var binance = new BinanceClient())
24 | {
25 | return (await binance.Spot.Market.Get24HPricesAsync()).Data.Where(x => x.Symbol.ToLower().Contains(quoteCurrency.ToLower())).Select(x => new Pair
26 | {
27 | TotalTradedQuoteAssetVolume = x.QuoteVolume,
28 | QuoteCurrency = quoteCurrency,
29 | BaseCurrency = ExtractBaseCurrency(x.Symbol, quoteCurrency)
30 | }).ToList();
31 | }
32 | }
33 |
34 | public async Task PlaceOrder(Pair pair, decimal amountToBuyInQuoteCurrency)
35 | {
36 | var result = new PlaceOrderResult();
37 | using (var binance = new BinanceClient())
38 | {
39 | var response = await binance.Spot.Order.PlaceOrderAsync(ToBinanceSymbol(pair.QuoteCurrency, pair.BaseCurrency), OrderSide.Buy, OrderType.Market, quoteOrderQuantity: amountToBuyInQuoteCurrency);
40 | if (response.Success)
41 | {
42 | result.Message = $"Market Buy Order placed: {response.Data.Quantity} {pair.BaseCurrency} / {response.Data.QuoteQuantityFilled} {pair.QuoteCurrency}";
43 | result.Success = true;
44 | }
45 | else
46 | {
47 | result.Message = $"Error while placing Order: {response.Error?.Message}";
48 | }
49 | }
50 | return result;
51 | }
52 |
53 | public string ExtractBaseCurrency(string symbol, string quoteCurrency)
54 | {
55 | return symbol.ToUpper().Replace(quoteCurrency.ToUpper(), "");
56 | }
57 |
58 | public string ToBinanceSymbol(string pairQuoteCurrency, string pairBaseCurrency)
59 | {
60 | return $"{pairBaseCurrency.ToUpper()}{pairQuoteCurrency.ToUpper()}";
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/ExchangeLayer/Implementations/Huobi.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using _3Commas.BotCreator.ExchangeLayer.Entities;
5 | using CryptoExchange.Net.Authentication;
6 | using Huobi.Net;
7 | using Huobi.Net.Objects;
8 |
9 | namespace _3Commas.BotCreator.ExchangeLayer.Implementations
10 | {
11 | public class Huobi : IExchange
12 | {
13 | public Huobi(Keys settings)
14 | {
15 | HuobiClient.SetDefaultOptions(new HuobiClientOptions { ApiCredentials = new ApiCredentials(settings.ApiKeyHuobi, settings.SecretHuobi) });
16 | }
17 | public string Name => "Huobi";
18 |
19 | public async Task> GetAllPairsByQuoteCurrency(string quoteCurrency)
20 | {
21 | var pairs = new List();
22 | using (var huobi = new HuobiClient())
23 | {
24 | var symbols = (await huobi.GetSymbolsAsync()).Data.Where(x => x.Symbol.ToLower().Contains(quoteCurrency.ToLower())).ToList();
25 | foreach (var huobiSymbol in symbols)
26 | {
27 | if (huobiSymbol.State == HuobiSymbolState.Online)
28 | {
29 | var symbolDetails = await huobi.GetSymbolDetails24HAsync(huobiSymbol.Symbol);
30 | if (symbolDetails.Data == null)
31 | {
32 | continue;
33 | }
34 |
35 | var pair = new Pair
36 | {
37 | TotalTradedQuoteAssetVolume = symbolDetails.Data.Volume.Value,
38 | QuoteCurrency = quoteCurrency,
39 | BaseCurrency = ExtractBaseCurrency(huobiSymbol.Symbol, quoteCurrency)
40 | };
41 | pairs.Add(pair);
42 | }
43 | }
44 | }
45 | return pairs;
46 | }
47 |
48 | public async Task PlaceOrder(Pair pair, decimal amountToBuyInQuoteCurrency)
49 | {
50 | var result = new PlaceOrderResult();
51 | using (var huobi = new HuobiClient())
52 | {
53 | var accounts = await huobi.GetAccountsAsync();
54 | var response = await huobi.PlaceOrderAsync(accounts.Data.Single().Id, ToHuobiSymbol(pair.QuoteCurrency, pair.BaseCurrency), HuobiOrderType.MarketBuy, amountToBuyInQuoteCurrency);
55 | if (response.Success)
56 | {
57 | result.Message = $"Market Buy Order placed. ID: {response.Data}";
58 | result.Success = true;
59 | }
60 | else
61 | {
62 | result.Message = $"Error while placing Order: {response.Error?.Message}";
63 | }
64 | }
65 | return result;
66 | }
67 |
68 | public string ExtractBaseCurrency(string symbol, string quoteCurrency)
69 | {
70 | return symbol.ToUpper().Replace(quoteCurrency.ToUpper(), "");
71 | }
72 |
73 | public string ToHuobiSymbol(string pairQuoteCurrency, string pairBaseCurrency)
74 | {
75 | return $"{pairBaseCurrency.ToUpper()}{pairQuoteCurrency.ToUpper()}";
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Infrastructure/IViewBase.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace _3Commas.BotCreator.Infrastructure
4 | {
5 | public interface IViewBase : IWin32Window
6 | {
7 | }
8 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Infrastructure/Presenter.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace _3Commas.BotCreator.Infrastructure
4 | {
5 | public abstract class PresenterBase where TView: IWin32Window
6 | {
7 | protected PresenterBase(TView view)
8 | {
9 | View = view;
10 | }
11 |
12 | protected TView View { get; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Keys.cs:
--------------------------------------------------------------------------------
1 | namespace _3Commas.BotCreator
2 | {
3 | public class Keys
4 | {
5 | public string ApiKey3Commas { get; set; }
6 | public string Secret3Commas { get; set; }
7 | public string ApiKeyBinance { get; set; }
8 | public string SecretBinance { get; set; }
9 | public string ApiKeyHuobi { get; set; }
10 | public string SecretHuobi { get; set; }
11 | }
12 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/MappingProfiles/BotSettingsProfile.cs:
--------------------------------------------------------------------------------
1 | using _3Commas.BotCreator.Services.BotSettingService;
2 | using _3Commas.BotCreator.Views.MainForm;
3 | using AutoMapper;
4 |
5 | namespace _3Commas.BotCreator.MappingProfiles
6 | {
7 | public class BotSettingsProfile : Profile
8 | {
9 | public BotSettingsProfile()
10 | {
11 | CreateMap().ReverseMap();
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Misc/AssemblyHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | namespace _3Commas.BotCreator.Misc
4 | {
5 | public static class AssemblyHelper
6 | {
7 | public static string AssemblyTitle
8 | {
9 | get
10 | {
11 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
12 | if (attributes.Length > 0)
13 | {
14 | AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
15 | if (titleAttribute.Title != "")
16 | {
17 | return titleAttribute.Title;
18 | }
19 | }
20 | return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
21 | }
22 | }
23 |
24 | public static string AssemblyVersion
25 | {
26 | get
27 | {
28 | return Assembly.GetExecutingAssembly().GetName().Version.ToString();
29 | }
30 | }
31 |
32 | public static string AssemblyDescription
33 | {
34 | get
35 | {
36 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
37 | if (attributes.Length == 0)
38 | {
39 | return "";
40 | }
41 | return ((AssemblyDescriptionAttribute)attributes[0]).Description;
42 | }
43 | }
44 |
45 | public static string AssemblyProduct
46 | {
47 | get
48 | {
49 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
50 | if (attributes.Length == 0)
51 | {
52 | return "";
53 | }
54 | return ((AssemblyProductAttribute)attributes[0]).Product;
55 | }
56 | }
57 |
58 | public static string AssemblyCopyright
59 | {
60 | get
61 | {
62 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
63 | if (attributes.Length == 0)
64 | {
65 | return "";
66 | }
67 | return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
68 | }
69 | }
70 |
71 | public static string AssemblyCompany
72 | {
73 | get
74 | {
75 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
76 | if (attributes.Length == 0)
77 | {
78 | return "";
79 | }
80 | return ((AssemblyCompanyAttribute)attributes[0]).Company;
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Misc/BotManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using _3Commas.BotCreator._3CommasLayer;
7 | using _3Commas.BotCreator.ExchangeLayer;
8 | using _3Commas.BotCreator.ExchangeLayer.Entities;
9 | using _3Commas.BotCreator.Views.MainForm;
10 | using Microsoft.Extensions.Logging;
11 | using XCommas.Net.Objects;
12 |
13 | namespace _3Commas.BotCreator.Misc
14 | {
15 | public class BotManager
16 | {
17 | private readonly ILogger _logger;
18 | private readonly IXCommasClient _xCommasClient;
19 | private readonly IExchange _exchange;
20 | private static string[] Stablecoins => new[] { "AUD", "EURS", "EBASE", "GBP", "USDT", "USDC", "DAI", "TUSD", "BUSD", "PAX", "HUSD", "SUSD", "USDK", "MUSD", "GUSD", "SAI", "EOSDT", "USDS", "BITCNY", "TRYB", "RSV", "BGBP", "QC", "USNBT", "BKRW", "THKD" };
21 |
22 | public BotManager(ILogger logger, IXCommasClient xCommasClient, IExchange exchange)
23 | {
24 | this._logger = logger;
25 | _xCommasClient = xCommasClient;
26 | _exchange = exchange;
27 | }
28 |
29 | public async Task> RetrieveAccounts()
30 | {
31 | var accounts = new List();
32 |
33 | var response = await _xCommasClient.GetAccountsAsync();
34 | _logger.LogInformation("Retrieving exchange information from 3Commas...");
35 | if (!response.IsSuccess)
36 | {
37 | this._logger.LogError("Problem with 3Commas connection: " + response.Error);
38 | }
39 | else
40 | {
41 | _logger.LogInformation($"{response.Data.Length} Exchanges found");
42 | accounts = response.Data.ToList();
43 | }
44 |
45 | return accounts;
46 | }
47 |
48 | public async Task> RetrieveBlacklistedPairs()
49 | {
50 | var pairs = new List();
51 |
52 | var response = await _xCommasClient.GetBotPairsBlackListAsync();
53 | _logger.LogInformation("Retrieving pairs blacklist from 3Commas...");
54 | if (!response.IsSuccess)
55 | {
56 | this._logger.LogError("Problem with 3Commas connection: " + response.Error);
57 | }
58 | else
59 | {
60 | _logger.LogInformation($"{response.Data.Pairs.Length} blacklisted pairs found");
61 | pairs = response.Data.Pairs.ToList();
62 | }
63 |
64 | return pairs;
65 | }
66 |
67 | public async Task PreviewBotsCreation(int numberOfBots, BotSettingViewModel settings, CancellationToken cancellationToken)
68 | {
69 | var existingBots = await GetAllBots();
70 | var blacklistedPairs = await RetrieveBlacklistedPairs();
71 |
72 | int created = 0;
73 |
74 | _logger.LogInformation($"Retrieving pairs from {_exchange.Name}...");
75 | var prices = await _exchange.GetAllPairsByQuoteCurrency(settings.QuoteCurrency);
76 | _logger.LogInformation($"{prices.Count} Pairs for {settings.QuoteCurrency.ToUpper()} found");
77 |
78 | foreach (var pair in prices.OrderByDescending(x => x.TotalTradedQuoteAssetVolume))
79 | {
80 | if (cancellationToken.IsCancellationRequested)
81 | {
82 | _logger.LogInformation($"Operation cancelled!");
83 | break;
84 | }
85 |
86 | var symbol = TransformPairTo3CommasSymbolString(pair.QuoteCurrency, pair.BaseCurrency);
87 |
88 | if (SymbolShouldBeSkipped(symbol, settings.SkipExistingPairs, settings.SkipBaseStablecoin, settings.Strategy, pair, existingBots, settings.SkipBlacklistedPairs, blacklistedPairs)) continue;
89 |
90 | var marketData = await _xCommasClient.GetCurrencyRateAsync(symbol);
91 | if (!marketData.IsSuccess)
92 | {
93 | if (!marketData.Error.StartsWith("Unknown pair"))
94 | {
95 | _logger.LogInformation($"Skipped Pair {symbol}. Reason: '{marketData.Error}'");
96 | }
97 | continue;
98 | }
99 |
100 | var botName = NameHelper.GenerateBotName(settings.Botname, symbol, settings.Strategy);
101 | _logger.LogInformation($"Bot to be created: '{botName}'");
102 |
103 | if (settings.BuyBaseCurrency && settings.BaseCurrencyToBuy > 0)
104 | {
105 | _logger.LogInformation($"Market Buy Order to be placed: {settings.BaseCurrencyToBuy} {pair.QuoteCurrency}");
106 | }
107 |
108 | created++;
109 |
110 | if (created == numberOfBots) break;
111 | }
112 |
113 | _logger.LogInformation($"{created} bots to be created");
114 | }
115 |
116 | public async Task CreateBots(int numberOfBots, bool enable, BotSettingViewModel settings, CancellationToken cancellationToken)
117 | {
118 | var existingBots = await GetAllBots();
119 | var blacklistedPairs = await RetrieveBlacklistedPairs();
120 |
121 | int created = 0;
122 |
123 | _logger.LogInformation($"Retrieving pairs from {_exchange.Name}...");
124 | var prices = await _exchange.GetAllPairsByQuoteCurrency(settings.QuoteCurrency);
125 | _logger.LogInformation($"{prices.Count} Pairs for {settings.QuoteCurrency.ToUpper()} found");
126 |
127 | foreach (var pair in prices.OrderByDescending(x => x.TotalTradedQuoteAssetVolume))
128 | {
129 | if (cancellationToken.IsCancellationRequested)
130 | {
131 | _logger.LogInformation($"Operation cancelled!");
132 | break;
133 | }
134 |
135 | var symbol = TransformPairTo3CommasSymbolString(pair.QuoteCurrency, pair.BaseCurrency);
136 |
137 | if (SymbolShouldBeSkipped(symbol, settings.SkipExistingPairs, settings.SkipBaseStablecoin, settings.Strategy, pair, existingBots, settings.SkipBlacklistedPairs, blacklistedPairs)) continue;
138 |
139 | var marketData = await _xCommasClient.GetCurrencyRateAsync(symbol);
140 | if (!marketData.IsSuccess)
141 | {
142 | if (!marketData.Error.StartsWith("Unknown pair"))
143 | {
144 | _logger.LogInformation($"Skipped Pair {symbol}. Reason: '{marketData.Error}'");
145 | }
146 | continue;
147 | }
148 |
149 | var botName = NameHelper.GenerateBotName(settings.Botname, symbol, settings.Strategy);
150 |
151 | var bot = CreateBot(botName, enable, symbol, settings);
152 | var response = await _xCommasClient.CreateBotAsync(settings.ExchangeAccountId.Value, settings.Strategy, bot);
153 | if (!response.IsSuccess)
154 | {
155 | _logger.LogError($"Could not create bot for {symbol}: {response.Error.Replace(Environment.NewLine, " ")}");
156 | continue;
157 | }
158 |
159 | if (enable)
160 | {
161 | var res = await _xCommasClient.EnableBotAsync(response.Data.Id);
162 | if (!res.IsSuccess)
163 | {
164 | _logger.LogError($"Bot '{botName}' created but there was an error with activation: {res.Error.Replace(Environment.NewLine, " ")}");
165 | }
166 | else
167 | {
168 | _logger.LogInformation($"Bot created and started: '{botName}'");
169 | }
170 | }
171 | else
172 | {
173 | _logger.LogInformation($"Bot created: '{botName}'");
174 | }
175 |
176 | if (settings.BuyBaseCurrency && settings.BaseCurrencyToBuy > 0)
177 | {
178 | var placeOrderResult = await _exchange.PlaceOrder(pair, settings.BaseCurrencyToBuy);
179 | if (placeOrderResult.Success)
180 | {
181 | _logger.LogInformation(placeOrderResult.Message);
182 | }
183 | else
184 | {
185 | _logger.LogError(placeOrderResult.Message);
186 | }
187 | }
188 |
189 | created++;
190 |
191 | if (created == numberOfBots) break;
192 | }
193 |
194 | _logger.LogInformation($"{created} bots created");
195 | }
196 |
197 | private bool SymbolShouldBeSkipped(string symbol, bool checkForExistingBots, bool checkForBaseStablecoin, Strategy strategy, Pair pair, List existingBots, bool checkForBlacklistedBots, List blacklistedPairs)
198 | {
199 | if (checkForBaseStablecoin && Stablecoins.Any(s => s.ToLower() == pair.BaseCurrency.ToLower()))
200 | {
201 | _logger.LogInformation($"Skipping {symbol} because base is a stablecoin");
202 | return true;
203 | }
204 |
205 | if (checkForBlacklistedBots && blacklistedPairs.Contains(symbol))
206 | {
207 | _logger.LogInformation($"Skipping {symbol} because it is blacklisted");
208 | return true;
209 | }
210 |
211 | if (IsSymbolIgnored(symbol))
212 | {
213 | _logger.LogInformation($"Skipping {symbol}");
214 | return true;
215 | }
216 |
217 | if (checkForExistingBots && existingBots.Any(x =>
218 | x.Pairs.Any(p => p.ToLower() == $"{pair.QuoteCurrency.ToLower()}_{pair.BaseCurrency.ToLower()}") &&
219 | x.Strategy == strategy))
220 | {
221 | _logger.LogInformation($"Bot for {strategy} {symbol} already exist");
222 | return true;
223 | }
224 |
225 | return false;
226 | }
227 |
228 | private Bot CreateBot(string botName, bool enabled, string symbol, BotSettingViewModel request)
229 | {
230 | var bot = new Bot();
231 |
232 | // Main Settings
233 | bot.Name = botName;
234 | bot.IsEnabled = enabled;
235 | bot.Type = "";
236 |
237 | // Pairs
238 | bot.Pairs = new[] { symbol };
239 | bot.MaxActiveDeals = 1;
240 |
241 | // Strategy
242 | bot.Strategy = request.Strategy;
243 | bot.ProfitCurrency = request.ProfitCurrency;
244 | bot.BaseOrderVolume = request.BaseOrderSize;
245 | bot.BaseOrderVolumeType = request.BaseOrderSizeType;
246 | bot.SafetyOrderVolume = request.SafetyOrderSize;
247 | bot.SafetyOrderVolumeType = request.SafetyOrderSizeType;
248 | bot.StartOrderType = request.StartOrderType;
249 |
250 | // Leverage
251 | bot.LeverageType = request.LeverageType;
252 | if (request.LeverageType != LeverageType.NotSpecified)
253 | {
254 | bot.LeverageCustomValue = request.LeverageCustomValue;
255 | }
256 |
257 | // Signals
258 | bot.Strategies = request.DealStartConditions;
259 |
260 | // Stop Loss
261 | decimal stopLossPercentage = 0;
262 | StopLossType stopLossType = StopLossType.StopLoss;
263 | bool stopLossTimeoutEnabled = false;
264 | int stopLossTimeoutInSeconds = 0;
265 | if (request.StopLossEnabled)
266 | {
267 | stopLossPercentage = request.StopLossPercentage;
268 | stopLossType = request.StopLossAction;
269 | stopLossTimeoutEnabled = request.StopLossTimeoutEnabled;
270 | stopLossTimeoutInSeconds = request.StopLossTimeout;
271 | }
272 |
273 | bot.StopLossPercentage = stopLossPercentage;
274 | bot.StopLossType = stopLossType;
275 | bot.StopLossTimeoutEnabled = stopLossTimeoutEnabled;
276 | bot.StopLossTimeoutInSeconds = stopLossTimeoutInSeconds;
277 |
278 | // TP
279 | bot.TakeProfitType = TakeProfitType.Total;
280 | bot.TakeProfit = request.TargetProfit;
281 |
282 | // Trailing
283 | bot.TrailingEnabled = request.TrailingEnabled;
284 | bot.TrailingDeviation = request.TrailingDeviation;
285 |
286 | // Safety orders
287 | bot.MaxSafetyOrders = request.MaxSafetyTradesCount;
288 | bot.ActiveSafetyOrdersCount = request.MaxActiveSafetyTradesCount;
289 | bot.SafetyOrderStepPercentage = request.PriceDeviationToOpenSafetyOrders;
290 | bot.MartingaleVolumeCoefficient = request.SafetyOrderVolumeScale;
291 | bot.MartingaleStepCoefficient = request.SafetyOrderStepScale;
292 |
293 | // Advanced
294 | bot.Cooldown = request.CooldownBetweenDeals;
295 | return bot;
296 | }
297 |
298 | private bool IsSymbolIgnored(string symbol)
299 | {
300 | return symbol.EndsWith("DOWN")
301 | || symbol.EndsWith("UP")
302 | || symbol.EndsWith("BULL")
303 | || symbol.EndsWith("BEAR");
304 | }
305 |
306 | public static string TransformPairTo3CommasSymbolString(string quoteCurrency, string baseCurrency)
307 | {
308 | return $"{quoteCurrency.ToUpper()}_{baseCurrency}";
309 | }
310 |
311 | private async Task> GetAllBots()
312 | {
313 | _logger.LogInformation("Retrieving existing Bots from 3commas...");
314 |
315 | var bots = new List();
316 | int take = 100;
317 | int skip = 0;
318 | while (true)
319 | {
320 | var result = await _xCommasClient.GetBotsAsync(limit: take, offset: skip);
321 | if (!result.IsSuccess)
322 | {
323 | throw new Exception("3Commas Connection Issue: " + result.Error);
324 | }
325 | if (result.Data.Length == 0)
326 | {
327 | break;
328 | }
329 |
330 | bots.AddRange(result.Data);
331 | skip += take;
332 | }
333 |
334 | _logger.LogInformation($"{bots.Count} Bots found");
335 | return bots;
336 | }
337 | }
338 | }
339 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Misc/ControlHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace _3Commas.BotCreator.Misc
5 | {
6 | public static class ControlHelper
7 | {
8 | public static void AddValuesToCombobox(ComboBox comboBox, TEnum defaultValue)
9 | {
10 | AddValuesToCombobox(comboBox);
11 | comboBox.Text = defaultValue.ToString();
12 | }
13 |
14 | public static void AddValuesToCombobox(ComboBox comboBox)
15 | {
16 | comboBox.DataSource = (TEnum[])Enum.GetValues(typeof(TEnum));
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Misc/NameHelper.cs:
--------------------------------------------------------------------------------
1 | using XCommas.Net.Objects;
2 |
3 | namespace _3Commas.BotCreator.Misc
4 | {
5 | public static class NameHelper
6 | {
7 | public static string GenerateBotName(string nameFormula, string symbol, Strategy strategy)
8 | {
9 | return nameFormula
10 | .Replace("{pair}", symbol)
11 | .Replace("{strategy}", strategy.ToString());
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Misc/TextBoxLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace _3Commas.BotCreator.Misc
6 | {
7 | internal class TextBoxLogger : ILogger
8 | {
9 | private readonly TextBox _txtOutput;
10 |
11 | public TextBoxLogger(TextBox txtOutput)
12 | {
13 | this._txtOutput = txtOutput;
14 | }
15 |
16 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter)
17 | {
18 | string message = "";
19 | if (formatter != null)
20 | {
21 | message += formatter(state, exception);
22 | }
23 |
24 | _txtOutput.AppendText($"{DateTime.Now} {logLevel}: {message}{Environment.NewLine}");
25 | }
26 |
27 | public bool IsEnabled(LogLevel logLevel)
28 | {
29 | return true;
30 | }
31 |
32 | public IDisposable BeginScope(TState state)
33 | {
34 | return new NoopDisposable();
35 | }
36 |
37 | private class NoopDisposable : IDisposable
38 | {
39 | public void Dispose()
40 | {
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Configuration;
3 | using System.Windows.Forms;
4 | using _3Commas.BotCreator.Infrastructure;
5 | using _3Commas.BotCreator.Services.BotSettingService;
6 | using _3Commas.BotCreator.Services.MessageBoxService;
7 | using _3Commas.BotCreator.Views.AboutBox;
8 | using _3Commas.BotCreator.Views.ChooseSignalView;
9 | using _3Commas.BotCreator.Views.MainForm;
10 | using _3Commas.BotCreator.Views.SaveTemplateView;
11 | using _3Commas.BotCreator.Views.SetApiKeyView;
12 | using AutoMapper;
13 | using Microsoft.Extensions.DependencyInjection;
14 |
15 | namespace _3Commas.BotCreator
16 | {
17 | static class Program
18 | {
19 | ///
20 | /// The main entry point for the application.
21 | ///
22 | [STAThread]
23 | static void Main()
24 | {
25 | Application.EnableVisualStyles();
26 | Application.SetCompatibleTextRenderingDefault(false);
27 |
28 | try
29 | {
30 | ConfigureServices();
31 | EncryptConfigFile();
32 | Application.Run((Form) ServiceProvider.GetRequiredService());
33 | }
34 | catch (Exception e)
35 | {
36 | MessageBox.Show("Sorry, but something went wrong!" + Environment.NewLine + Environment.NewLine +
37 | "Please let me know that there was a problem and I will try to fit it for you. You can report this error here: " +
38 | "https://github.com/MarcDrexler/3Commas.BotCreator/issues" + Environment.NewLine + Environment.NewLine +
39 | "Error Details: " + Environment.NewLine +
40 | e.ToString(), "Sorry!", MessageBoxButtons.OK, MessageBoxIcon.Error);
41 | }
42 | }
43 |
44 | internal static IServiceProvider ServiceProvider { get; set; }
45 |
46 | static void ConfigureServices()
47 | {
48 | var services = new ServiceCollection();
49 | services.AddSingleton();
50 | services.AddTransient();
51 | services.AddTransient();
52 | services.AddTransient();
53 | services.AddTransient();
54 | services.AddSingleton();
55 | services.AddTransient();
56 |
57 | var config = new MapperConfiguration(cfg => cfg.AddMaps(typeof(Program)));
58 | services.AddSingleton(config.CreateMapper());
59 | ServiceProvider = services.BuildServiceProvider();
60 | }
61 |
62 | private static void EncryptConfigFile()
63 | {
64 | System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
65 | try
66 | {
67 | ConfigurationSection section = config.GetSection("userSettings/_3Commas.BotCreator.Properties.Settings");
68 | if (!section.SectionInformation.IsProtected)
69 | {
70 | section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
71 | section.SectionInformation.ForceSave = true;
72 | config.Save(ConfigurationSaveMode.Full);
73 | }
74 | }
75 | catch (Exception e)
76 | {
77 | MessageBox.Show($"Could not encrypt config file at {config.FilePath}!{Environment.NewLine}{Environment.NewLine}The Error was: {e.Message} {Environment.NewLine}{Environment.NewLine}Your api key might be stored in cleartext. Make sure to remove it from the config file if you don't need it anymore.", "Notice", MessageBoxButtons.OK, MessageBoxIcon.Warning);
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Bot Creator")]
8 | [assembly: AssemblyDescription("3Commas Bulk Bot Creation Tool.")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Bot Creator")]
12 | [assembly: AssemblyCopyright("Copyright © 2021")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("02d16940-5926-45ea-a706-21b8d3e1f643")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.18.0.0")]
35 | [assembly: AssemblyFileVersion("1.18.0.0")]
36 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Properties/DataSources/_3Commas.BotCreator.Views.MainForm.BotSettings.datasource:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | _3Commas.BotCreator.Views.MainForm.BotSettings, BotCreator, Version=1.9.0.0, Culture=neutral, PublicKeyToken=null
10 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace _3Commas.BotCreator.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("_3Commas.BotCreator.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap _3Commas {
67 | get {
68 | object obj = ResourceManager.GetObject("_3Commas", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | internal static System.Drawing.Bitmap Add_16x16 {
77 | get {
78 | object obj = ResourceManager.GetObject("Add_16x16", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | internal static System.Drawing.Bitmap Binance {
87 | get {
88 | object obj = ResourceManager.GetObject("Binance", resourceCulture);
89 | return ((System.Drawing.Bitmap)(obj));
90 | }
91 | }
92 |
93 | ///
94 | /// Looks up a localized resource of type System.Drawing.Bitmap.
95 | ///
96 | internal static System.Drawing.Bitmap Cancel_16x16 {
97 | get {
98 | object obj = ResourceManager.GetObject("Cancel_16x16", resourceCulture);
99 | return ((System.Drawing.Bitmap)(obj));
100 | }
101 | }
102 |
103 | ///
104 | /// Looks up a localized resource of type System.Drawing.Bitmap.
105 | ///
106 | internal static System.Drawing.Bitmap Clear_16x16 {
107 | get {
108 | object obj = ResourceManager.GetObject("Clear_16x16", resourceCulture);
109 | return ((System.Drawing.Bitmap)(obj));
110 | }
111 | }
112 |
113 | ///
114 | /// Looks up a localized resource of type System.Drawing.Bitmap.
115 | ///
116 | internal static System.Drawing.Bitmap Close_16x16 {
117 | get {
118 | object obj = ResourceManager.GetObject("Close_16x16", resourceCulture);
119 | return ((System.Drawing.Bitmap)(obj));
120 | }
121 | }
122 |
123 | ///
124 | /// Looks up a localized resource of type System.Drawing.Bitmap.
125 | ///
126 | internal static System.Drawing.Bitmap download {
127 | get {
128 | object obj = ResourceManager.GetObject("download", resourceCulture);
129 | return ((System.Drawing.Bitmap)(obj));
130 | }
131 | }
132 |
133 | ///
134 | /// Looks up a localized resource of type System.Drawing.Bitmap.
135 | ///
136 | internal static System.Drawing.Bitmap Huobi {
137 | get {
138 | object obj = ResourceManager.GetObject("Huobi", resourceCulture);
139 | return ((System.Drawing.Bitmap)(obj));
140 | }
141 | }
142 |
143 | ///
144 | /// Looks up a localized resource of type System.Drawing.Bitmap.
145 | ///
146 | internal static System.Drawing.Bitmap Info_16x16 {
147 | get {
148 | object obj = ResourceManager.GetObject("Info_16x16", resourceCulture);
149 | return ((System.Drawing.Bitmap)(obj));
150 | }
151 | }
152 |
153 | ///
154 | /// Looks up a localized resource of type System.Drawing.Bitmap.
155 | ///
156 | internal static System.Drawing.Bitmap Info_32x32 {
157 | get {
158 | object obj = ResourceManager.GetObject("Info_32x32", resourceCulture);
159 | return ((System.Drawing.Bitmap)(obj));
160 | }
161 | }
162 |
163 | ///
164 | /// Looks up a localized resource of type System.Drawing.Bitmap.
165 | ///
166 | internal static System.Drawing.Bitmap Play_16x16 {
167 | get {
168 | object obj = ResourceManager.GetObject("Play_16x16", resourceCulture);
169 | return ((System.Drawing.Bitmap)(obj));
170 | }
171 | }
172 |
173 | ///
174 | /// Looks up a localized resource of type System.Drawing.Bitmap.
175 | ///
176 | internal static System.Drawing.Bitmap Properties_32x32 {
177 | get {
178 | object obj = ResourceManager.GetObject("Properties_32x32", resourceCulture);
179 | return ((System.Drawing.Bitmap)(obj));
180 | }
181 | }
182 |
183 | ///
184 | /// Looks up a localized resource of type System.Drawing.Bitmap.
185 | ///
186 | internal static System.Drawing.Bitmap Save_16x16 {
187 | get {
188 | object obj = ResourceManager.GetObject("Save_16x16", resourceCulture);
189 | return ((System.Drawing.Bitmap)(obj));
190 | }
191 | }
192 |
193 | ///
194 | /// Looks up a localized resource of type System.Drawing.Bitmap.
195 | ///
196 | internal static System.Drawing.Bitmap ShowTestReport_16x16 {
197 | get {
198 | object obj = ResourceManager.GetObject("ShowTestReport_16x16", resourceCulture);
199 | return ((System.Drawing.Bitmap)(obj));
200 | }
201 | }
202 |
203 | ///
204 | /// Looks up a localized resource of type System.Drawing.Bitmap.
205 | ///
206 | internal static System.Drawing.Bitmap Suggestion_16x16 {
207 | get {
208 | object obj = ResourceManager.GetObject("Suggestion_16x16", resourceCulture);
209 | return ((System.Drawing.Bitmap)(obj));
210 | }
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\Info_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\Resources\Save_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
128 | ..\resources\huobi.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
131 | ..\resources\binance.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
132 |
133 |
134 | ..\Resources\Suggestion_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
135 |
136 |
137 | ..\Resources\ShowTestReport_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
138 |
139 |
140 | ..\Resources\Play_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
141 |
142 |
143 | ..\Resources\Add_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
144 |
145 |
146 | ..\Resources\Properties_32x32.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
147 |
148 |
149 | ..\Resources\Info_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
150 |
151 |
152 | ..\Resources\Cancel_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
153 |
154 |
155 | ..\resources\3commas.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
156 |
157 |
158 | ..\Resources\download.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
159 |
160 |
161 | ..\Resources\Clear_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
162 |
163 |
164 | ..\Resources\Close_16x16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
165 |
166 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace _3Commas.BotCreator.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.UserScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("")]
29 | public string ApiKey3Commas {
30 | get {
31 | return ((string)(this["ApiKey3Commas"]));
32 | }
33 | set {
34 | this["ApiKey3Commas"] = value;
35 | }
36 | }
37 |
38 | [global::System.Configuration.UserScopedSettingAttribute()]
39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
40 | [global::System.Configuration.DefaultSettingValueAttribute("")]
41 | public string ApiKeyBinance {
42 | get {
43 | return ((string)(this["ApiKeyBinance"]));
44 | }
45 | set {
46 | this["ApiKeyBinance"] = value;
47 | }
48 | }
49 |
50 | [global::System.Configuration.UserScopedSettingAttribute()]
51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
52 | [global::System.Configuration.DefaultSettingValueAttribute("")]
53 | public string ApiKeyHuobi {
54 | get {
55 | return ((string)(this["ApiKeyHuobi"]));
56 | }
57 | set {
58 | this["ApiKeyHuobi"] = value;
59 | }
60 | }
61 |
62 | [global::System.Configuration.UserScopedSettingAttribute()]
63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
64 | [global::System.Configuration.DefaultSettingValueAttribute("")]
65 | public string Secret3Commas {
66 | get {
67 | return ((string)(this["Secret3Commas"]));
68 | }
69 | set {
70 | this["Secret3Commas"] = value;
71 | }
72 | }
73 |
74 | [global::System.Configuration.UserScopedSettingAttribute()]
75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
76 | [global::System.Configuration.DefaultSettingValueAttribute("")]
77 | public string SecretBinance {
78 | get {
79 | return ((string)(this["SecretBinance"]));
80 | }
81 | set {
82 | this["SecretBinance"] = value;
83 | }
84 | }
85 |
86 | [global::System.Configuration.UserScopedSettingAttribute()]
87 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
88 | [global::System.Configuration.DefaultSettingValueAttribute("")]
89 | public string SecretHuobi {
90 | get {
91 | return ((string)(this["SecretHuobi"]));
92 | }
93 | set {
94 | this["SecretHuobi"] = value;
95 | }
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/3Commas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/3Commas.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Add_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Add_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Binance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Binance.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Cancel_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Cancel_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Clear_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Clear_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Close_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Close_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Huobi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Huobi.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Info_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Info_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Info_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Info_32x32.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Play_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Play_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Properties_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Properties_32x32.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Save_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Save_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/ShowTestReport_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/ShowTestReport_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/Suggestion_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/Suggestion_16x16.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Resources/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/Resources/download.png
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Services/BotSettingService/BotSettingService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using _3Commas.BotCreator.Services.MessageBoxService;
6 | using Newtonsoft.Json;
7 |
8 | namespace _3Commas.BotCreator.Services.BotSettingService
9 | {
10 | public class BotSettingService : IBotSettingService
11 | {
12 | private readonly IMessageBoxService _mbs;
13 | private readonly List _settings;
14 | private readonly string _filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "BotCreator", "botsettings.json");
15 |
16 | public BotSettingService(IMessageBoxService mbs)
17 | {
18 | _mbs = mbs;
19 | var directory = Path.GetDirectoryName(_filePath);
20 | if (!Directory.Exists(directory))
21 | {
22 | Directory.CreateDirectory(directory);
23 | }
24 | if (File.Exists(_filePath))
25 | {
26 | var json = File.ReadAllText(_filePath);
27 | _settings = JsonConvert.DeserializeObject>(json, new JsonSerializerSettings
28 | {
29 | TypeNameHandling = TypeNameHandling.All
30 | });
31 | }
32 | else
33 | {
34 | _settings = new List();
35 | }
36 | }
37 |
38 | public List GetAll()
39 | {
40 | return _settings.OrderByDescending(x => x.UpdatedAt).ToList();
41 | }
42 |
43 | public BotSettings GetByName(string name)
44 | {
45 | return _settings.Single(x => x.Name == name);
46 | }
47 |
48 | public bool Insert(BotSettings settings, string name)
49 | {
50 | try
51 | {
52 | settings.Id = Guid.NewGuid();
53 | settings.Name = name;
54 | settings.UpdatedAt = DateTime.Now;
55 | _settings.Add(settings);
56 | Save();
57 | return true;
58 | }
59 | catch (Exception e)
60 | {
61 | _mbs.ShowError(e.ToString());
62 | return false;
63 | }
64 | }
65 |
66 | public bool Update(BotSettings settings, Guid id)
67 | {
68 | try
69 | {
70 | var oldSettings = _settings.Single(x => x.Id == id);
71 | _settings.Remove(oldSettings);
72 | settings.Id = Guid.NewGuid();
73 | settings.UpdatedAt = DateTime.Now;
74 | settings.Name = oldSettings.Name;
75 | _settings.Add(settings);
76 | Save();
77 | return true;
78 | }
79 | catch (Exception e)
80 | {
81 | _mbs.ShowError(e.ToString());
82 | return false;
83 | }
84 | }
85 |
86 | public bool Delete(Guid id)
87 | {
88 | try
89 | {
90 | _settings.RemoveAll(s => s.Id == id);
91 | Save();
92 | return true;
93 | }
94 | catch (Exception e)
95 | {
96 | _mbs.ShowError(e.ToString());
97 | return false;
98 | }
99 | }
100 |
101 | private void Save()
102 | {
103 | File.WriteAllText(_filePath, JsonConvert.SerializeObject(_settings, Formatting.Indented, new JsonSerializerSettings
104 | {
105 | TypeNameHandling = TypeNameHandling.All
106 | }));
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Services/BotSettingService/BotSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using XCommas.Net.Objects;
4 |
5 | namespace _3Commas.BotCreator.Services.BotSettingService
6 | {
7 | public class BotSettings
8 | {
9 | public Guid Id { get; set; }
10 |
11 | public DateTime UpdatedAt { get; set; }
12 |
13 | public string Name { get; set; }
14 |
15 | public bool SkipExistingPairs { get; set; }
16 |
17 | public bool SkipBlacklistedPairs { get; set; }
18 |
19 | public bool SkipBaseStablecoin { get; set; }
20 |
21 | public string QuoteCurrency { get; set; }
22 |
23 | public string Botname { get; set; }
24 |
25 | public int? ExchangeAccountId { get; set; }
26 |
27 | public string Strategy { get; set; }
28 |
29 | public string ProfitCurrency { get; set; }
30 |
31 | public decimal BaseOrderSize { get; set; }
32 |
33 | public string BaseOrderSizeType { get; set; }
34 |
35 | public decimal SafetyOrderSize { get; set; }
36 |
37 | public string SafetyOrderSizeType { get; set; }
38 |
39 | public string StartOrderType { get; set; }
40 |
41 | public decimal TargetProfit { get; set; }
42 |
43 | public string TargetProfitType { get; set; }
44 |
45 | public bool TrailingEnabled { get; set; }
46 |
47 | public decimal TrailingDeviation { get; set; }
48 |
49 | public List DealStartConditions { get; set; } = new List();
50 |
51 | public bool StopLossEnabled { get; set; }
52 |
53 | public decimal StopLossPercentage { get; set; }
54 |
55 | public string StopLossAction { get; set; }
56 |
57 | public bool StopLossTimeoutEnabled { get; set; }
58 |
59 | public int StopLossTimeout { get; set; }
60 |
61 | public int MaxSafetyTradesCount { get; set; }
62 |
63 | public int MaxActiveSafetyTradesCount { get; set; }
64 |
65 | public decimal PriceDeviationToOpenSafetyOrders { get; set; }
66 |
67 | public decimal SafetyOrderVolumeScale { get; set; }
68 |
69 | public decimal SafetyOrderStepScale { get; set; }
70 |
71 | public decimal DontStartDealIfDailyVolumeIsLessThan { get; set; }
72 |
73 | public decimal? MinimumPriceToOpenDeal { get; set; }
74 |
75 | public decimal? MaxPriceToOpenDeal { get; set; }
76 |
77 | public int CooldownBetweenDeals { get; set; }
78 |
79 | public int? OpenDealsAndStopCount { get; set; }
80 |
81 | public string LeverageType { get; set; }
82 |
83 | public int LeverageCustomValue { get; set; }
84 |
85 | public bool BuyBaseCurrency { get; set; }
86 |
87 | public decimal BaseCurrencyToBuy { get; set; }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Services/BotSettingService/IBotSettingService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace _3Commas.BotCreator.Services.BotSettingService
5 | {
6 | public interface IBotSettingService
7 | {
8 | List GetAll();
9 |
10 | BotSettings GetByName(string name);
11 |
12 | bool Insert(BotSettings settings, string name);
13 |
14 | bool Update(BotSettings settings, Guid id);
15 |
16 | bool Delete(Guid id);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Services/MessageBoxService/IMessageBoxService.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace _3Commas.BotCreator.Services.MessageBoxService
4 | {
5 | public interface IMessageBoxService
6 | {
7 | DialogResult ShowError(string text, string title = "Error");
8 | DialogResult ShowQuestion(string text);
9 | DialogResult ShowInformation(string text);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Services/MessageBoxService/MessageBoxService.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace _3Commas.BotCreator.Services.MessageBoxService
4 | {
5 | public class MessageBoxService : IMessageBoxService
6 | {
7 | public DialogResult ShowError(string text, string title = "Error")
8 | {
9 | return MessageBox.Show(text, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
10 | }
11 |
12 | public DialogResult ShowQuestion(string text)
13 | {
14 | return MessageBox.Show(text, "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
15 | }
16 |
17 | public DialogResult ShowInformation(string text)
18 | {
19 | return MessageBox.Show(text, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/AboutBox/AboutBox.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace _3Commas.BotCreator.Views.AboutBox
2 | {
3 | partial class AboutBox
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | protected override void Dispose(bool disposing)
14 | {
15 | if (disposing && (components != null))
16 | {
17 | components.Dispose();
18 | }
19 | base.Dispose(disposing);
20 | }
21 |
22 | #region Windows Form Designer generated code
23 |
24 | ///
25 | /// Required method for Designer support - do not modify
26 | /// the contents of this method with the code editor.
27 | ///
28 | private void InitializeComponent()
29 | {
30 | this.labelProductName = new System.Windows.Forms.Label();
31 | this.labelVersion = new System.Windows.Forms.Label();
32 | this.textBoxDescription = new System.Windows.Forms.TextBox();
33 | this.okButton = new System.Windows.Forms.Button();
34 | this.pbBuyMeACoffee = new System.Windows.Forms.PictureBox();
35 | this.linkGithubProject = new System.Windows.Forms.LinkLabel();
36 | this.label1 = new System.Windows.Forms.Label();
37 | ((System.ComponentModel.ISupportInitialize)(this.pbBuyMeACoffee)).BeginInit();
38 | this.SuspendLayout();
39 | //
40 | // labelProductName
41 | //
42 | this.labelProductName.Location = new System.Drawing.Point(15, 26);
43 | this.labelProductName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
44 | this.labelProductName.MaximumSize = new System.Drawing.Size(408, 17);
45 | this.labelProductName.Name = "labelProductName";
46 | this.labelProductName.Size = new System.Drawing.Size(408, 17);
47 | this.labelProductName.TabIndex = 19;
48 | this.labelProductName.Text = "Product Name";
49 | this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
50 | //
51 | // labelVersion
52 | //
53 | this.labelVersion.Location = new System.Drawing.Point(15, 49);
54 | this.labelVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
55 | this.labelVersion.MaximumSize = new System.Drawing.Size(408, 17);
56 | this.labelVersion.Name = "labelVersion";
57 | this.labelVersion.Size = new System.Drawing.Size(408, 17);
58 | this.labelVersion.TabIndex = 0;
59 | this.labelVersion.Text = "Version";
60 | this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
61 | //
62 | // textBoxDescription
63 | //
64 | this.textBoxDescription.Location = new System.Drawing.Point(15, 84);
65 | this.textBoxDescription.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
66 | this.textBoxDescription.Multiline = true;
67 | this.textBoxDescription.Name = "textBoxDescription";
68 | this.textBoxDescription.ReadOnly = true;
69 | this.textBoxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both;
70 | this.textBoxDescription.Size = new System.Drawing.Size(408, 60);
71 | this.textBoxDescription.TabIndex = 23;
72 | this.textBoxDescription.TabStop = false;
73 | this.textBoxDescription.Text = "Description";
74 | //
75 | // okButton
76 | //
77 | this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
78 | this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
79 | this.okButton.Location = new System.Drawing.Point(348, 249);
80 | this.okButton.Name = "okButton";
81 | this.okButton.Size = new System.Drawing.Size(75, 23);
82 | this.okButton.TabIndex = 24;
83 | this.okButton.Text = "&OK";
84 | //
85 | // pbBuyMeACoffee
86 | //
87 | this.pbBuyMeACoffee.Cursor = System.Windows.Forms.Cursors.Hand;
88 | this.pbBuyMeACoffee.Image = global::_3Commas.BotCreator.Properties.Resources.download;
89 | this.pbBuyMeACoffee.Location = new System.Drawing.Point(15, 231);
90 | this.pbBuyMeACoffee.Name = "pbBuyMeACoffee";
91 | this.pbBuyMeACoffee.Size = new System.Drawing.Size(165, 41);
92 | this.pbBuyMeACoffee.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
93 | this.pbBuyMeACoffee.TabIndex = 47;
94 | this.pbBuyMeACoffee.TabStop = false;
95 | this.pbBuyMeACoffee.Click += new System.EventHandler(this.pbBuyMeACoffee_Click);
96 | //
97 | // linkGithubProject
98 | //
99 | this.linkGithubProject.AutoSize = true;
100 | this.linkGithubProject.Location = new System.Drawing.Point(390, 154);
101 | this.linkGithubProject.Name = "linkGithubProject";
102 | this.linkGithubProject.Size = new System.Drawing.Size(28, 13);
103 | this.linkGithubProject.TabIndex = 50;
104 | this.linkGithubProject.TabStop = true;
105 | this.linkGithubProject.Text = "here";
106 | this.linkGithubProject.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkGithubProject_LinkClicked);
107 | //
108 | // label1
109 | //
110 | this.label1.AutoSize = true;
111 | this.label1.Location = new System.Drawing.Point(23, 154);
112 | this.label1.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
113 | this.label1.MaximumSize = new System.Drawing.Size(408, 17);
114 | this.label1.Name = "label1";
115 | this.label1.Size = new System.Drawing.Size(370, 13);
116 | this.label1.TabIndex = 51;
117 | this.label1.Text = "If you think something is broken or have any questions, please open an Issue";
118 | this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
119 | //
120 | // AboutBox
121 | //
122 | this.AcceptButton = this.okButton;
123 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
124 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
125 | this.ClientSize = new System.Drawing.Size(435, 284);
126 | this.Controls.Add(this.linkGithubProject);
127 | this.Controls.Add(this.label1);
128 | this.Controls.Add(this.labelProductName);
129 | this.Controls.Add(this.labelVersion);
130 | this.Controls.Add(this.okButton);
131 | this.Controls.Add(this.pbBuyMeACoffee);
132 | this.Controls.Add(this.textBoxDescription);
133 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
134 | this.MaximizeBox = false;
135 | this.MinimizeBox = false;
136 | this.Name = "AboutBox";
137 | this.Padding = new System.Windows.Forms.Padding(9);
138 | this.ShowIcon = false;
139 | this.ShowInTaskbar = false;
140 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
141 | this.Text = "About";
142 | ((System.ComponentModel.ISupportInitialize)(this.pbBuyMeACoffee)).EndInit();
143 | this.ResumeLayout(false);
144 | this.PerformLayout();
145 |
146 | }
147 |
148 | #endregion
149 | private System.Windows.Forms.Label labelProductName;
150 | private System.Windows.Forms.Label labelVersion;
151 | private System.Windows.Forms.TextBox textBoxDescription;
152 | private System.Windows.Forms.Button okButton;
153 | private System.Windows.Forms.PictureBox pbBuyMeACoffee;
154 | private System.Windows.Forms.LinkLabel linkGithubProject;
155 | private System.Windows.Forms.Label label1;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/AboutBox/AboutBox.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Reflection;
4 | using System.Windows.Forms;
5 |
6 | namespace _3Commas.BotCreator.Views.AboutBox
7 | {
8 | partial class AboutBox : Form
9 | {
10 | public AboutBox()
11 | {
12 | InitializeComponent();
13 | this.Text = String.Format("About {0}", AssemblyTitle);
14 | this.labelProductName.Text = AssemblyProduct;
15 | this.labelVersion.Text = String.Format("Version {0}", AssemblyVersion);
16 | this.textBoxDescription.Text = AssemblyDescription;
17 | }
18 |
19 | #region Assembly Attribute Accessors
20 |
21 | public string AssemblyTitle
22 | {
23 | get
24 | {
25 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
26 | if (attributes.Length > 0)
27 | {
28 | AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
29 | if (titleAttribute.Title != "")
30 | {
31 | return titleAttribute.Title;
32 | }
33 | }
34 | return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
35 | }
36 | }
37 |
38 | public string AssemblyVersion
39 | {
40 | get
41 | {
42 | return Assembly.GetExecutingAssembly().GetName().Version.ToString();
43 | }
44 | }
45 |
46 | public string AssemblyDescription
47 | {
48 | get
49 | {
50 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
51 | if (attributes.Length == 0)
52 | {
53 | return "";
54 | }
55 | return ((AssemblyDescriptionAttribute)attributes[0]).Description;
56 | }
57 | }
58 |
59 | public string AssemblyProduct
60 | {
61 | get
62 | {
63 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
64 | if (attributes.Length == 0)
65 | {
66 | return "";
67 | }
68 | return ((AssemblyProductAttribute)attributes[0]).Product;
69 | }
70 | }
71 |
72 | public string AssemblyCopyright
73 | {
74 | get
75 | {
76 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
77 | if (attributes.Length == 0)
78 | {
79 | return "";
80 | }
81 | return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
82 | }
83 | }
84 |
85 | public string AssemblyCompany
86 | {
87 | get
88 | {
89 | object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
90 | if (attributes.Length == 0)
91 | {
92 | return "";
93 | }
94 | return ((AssemblyCompanyAttribute)attributes[0]).Company;
95 | }
96 | }
97 | #endregion
98 |
99 | private void pbBuyMeACoffee_Click(object sender, EventArgs e)
100 | {
101 | Process.Start("https://www.buymeacoffee.com/marcdrexler");
102 | }
103 |
104 | private void linkGithubProject_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
105 | {
106 | Process.Start("https://github.com/MarcDrexler/3Commas.BotCreator/issues/new");
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/AboutBox/AboutBox.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/ChooseSignalView/ChooseSignalView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using _3Commas.BotCreator.Misc;
4 | using XCommas.Net.Objects;
5 |
6 | namespace _3Commas.BotCreator.Views.ChooseSignalView
7 | {
8 | public partial class ChooseSignalView : Form
9 | {
10 | private BotStrategy _strategy;
11 |
12 | public ChooseSignalView()
13 | {
14 | InitializeComponent();
15 |
16 | ControlHelper.AddValuesToCombobox(cmbTradingViewTime);
17 | ControlHelper.AddValuesToCombobox(cmbTradingViewType);
18 | ControlHelper.AddValuesToCombobox(cmbRsiTime);
19 | ControlHelper.AddValuesToCombobox(cmbUltTime);
20 | ControlHelper.AddValuesToCombobox(cmbTaPresetsTime);
21 | ControlHelper.AddValuesToCombobox(cmbTaPresetsType);
22 | }
23 |
24 | public BotStrategy Strategy => _strategy;
25 |
26 | private void radioButton_CheckedChanged(object sender, EventArgs e)
27 | {
28 | panelRsi.Visible = (sender == radioButtonRsi);
29 | panelUlt.Visible = (sender == radioButtonUlt);
30 | panelTaPresets.Visible = (sender == radioButtonTaPresets);
31 | panelTradingView.Visible = (sender == radioButtonTradingView);
32 | txtCustom.Visible = (sender == radioButtonCustom);
33 | }
34 |
35 | private void okButton_Click(object sender, EventArgs e)
36 | {
37 | if (radioButtonRsi.Checked)
38 | {
39 | if (cmbRsiTime.SelectedItem == null)
40 | {
41 | MessageBox.Show("Time is missing");
42 | return;
43 | }
44 |
45 | IndicatorTime.TryParse(cmbRsiTime.SelectedItem.ToString(), out IndicatorTime time);
46 | _strategy = new RsiBotStrategy { Options = new RsiOptions(time, (int)numRsiPoints.Value) };
47 | }
48 |
49 | if (radioButtonUlt.Checked)
50 | {
51 | if (cmbUltTime.SelectedItem == null)
52 | {
53 | MessageBox.Show("Time is missing");
54 | return;
55 | }
56 |
57 | IndicatorTime.TryParse(cmbUltTime.SelectedItem.ToString(), out IndicatorTime time);
58 | _strategy = new UltBotStrategy { Options = new UltOptions(time, (int)numUltPoints.Value) };
59 | }
60 |
61 | if (radioButtonTradingView.Checked)
62 | {
63 | if (cmbTradingViewTime.SelectedItem == null)
64 | {
65 | MessageBox.Show("Time is missing");
66 | return;
67 | }
68 | if (cmbTradingViewType.SelectedItem == null)
69 | {
70 | MessageBox.Show("Type is missing");
71 | return;
72 | }
73 |
74 | TradingViewTime.TryParse(cmbTradingViewTime.SelectedItem.ToString(), out TradingViewTime time);
75 | TradingViewIndicatorType.TryParse(cmbTradingViewType.SelectedItem.ToString(), out TradingViewIndicatorType type);
76 | _strategy = new TradingViewBotStrategy { Options = new TradingViewOptions(type, time) };
77 | }
78 |
79 | if (radioButtonTaPresets.Checked)
80 | {
81 | if (cmbTaPresetsTime.SelectedItem == null)
82 | {
83 | MessageBox.Show("Time is missing");
84 | return;
85 | }
86 | if (cmbTaPresetsType.SelectedItem == null)
87 | {
88 | MessageBox.Show("Type is missing");
89 | return;
90 | }
91 |
92 | IndicatorTime.TryParse(cmbTaPresetsTime.SelectedItem.ToString(), out IndicatorTime time);
93 | TaPresetsType.TryParse(cmbTaPresetsType.SelectedItem.ToString(), out TaPresetsType type);
94 | _strategy = new TaPresetsBotStrategy { Options = new TaPresetsOptions(type, time) };
95 | }
96 |
97 | if (radioButtonManual.Checked) _strategy = new ManualStrategy();
98 | if (radioButtonNonstop.Checked) _strategy = new NonStopBotStrategy();
99 |
100 | if (radioButtonCustom.Checked)
101 | {
102 | if (string.IsNullOrWhiteSpace(txtCustom.Text))
103 | {
104 | MessageBox.Show("Name is missing");
105 | return;
106 | }
107 | _strategy = new UnknownStrategy(txtCustom.Text);
108 | }
109 |
110 | this.DialogResult = DialogResult.OK;
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/ChooseSignalView/ChooseSignalView.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/MainForm/BotSettingViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using XCommas.Net.Objects;
4 |
5 | namespace _3Commas.BotCreator.Views.MainForm
6 | {
7 | public class BotSettingViewModel
8 | {
9 | public BotSettingViewModel()
10 | {
11 | // Default Values
12 | SkipExistingPairs = true;
13 | SkipBaseStablecoin = true;
14 | SkipBlacklistedPairs = true;
15 | QuoteCurrency = "USDT";
16 | Botname = "{strategy} {pair} Bot";
17 | BaseOrderSize = 10;
18 | SafetyOrderSize = 11;
19 | TargetProfit = 1;
20 | MaxSafetyTradesCount = 1;
21 | MaxActiveSafetyTradesCount = 1;
22 | PriceDeviationToOpenSafetyOrders = 1;
23 | SafetyOrderVolumeScale = 1;
24 | SafetyOrderStepScale = 1;
25 | CooldownBetweenDeals = 0;
26 | StartOrderType = StartOrderType.Limit;
27 | Strategy = Strategy.Long;
28 | StopLossAction = StopLossType.StopLoss;
29 | StopLossPercentage = (decimal) 1;
30 | StopLossTimeout = 1;
31 | LeverageType = LeverageType.NotSpecified;
32 | LeverageCustomValue = 1;
33 | ProfitCurrency = ProfitCurrency.QuoteCurrency;
34 | BaseOrderSizeType = VolumeType.QuoteCurrency;
35 | SafetyOrderSizeType = VolumeType.QuoteCurrency;
36 | }
37 |
38 | public BotSettingViewModel(LeverageType leverageType, int customLeverageValue, decimal stopLossPercentage, StopLossType stopLossType, bool stopLossTimeoutEnabled, int stopLossTimeoutInSeconds, bool checkForExistingBots, bool checkForBlacklistedPairs, bool checkForBaseStablecoin, string quoteCurrency, Strategy strategy, StartOrderType startOrderType, int maxSafetyOrders, int activeSafetyOrdersCount, decimal safetyOrderStepPercentage, decimal martingaleVolumeCoefficient, decimal martingaleStepCoefficient, decimal takeProfitPercentage, bool trailingEnabled, decimal trailingDeviation, string nameFormula, decimal baseOrderVolume, decimal safetyOrderVolume, List dealStartConditions, int cooldownBetweenDeals, int accountId, decimal amountToBuyInQuoteCurrency)
39 | {
40 | LeverageType = leverageType;
41 | LeverageCustomValue = customLeverageValue;
42 | StopLossPercentage = stopLossPercentage;
43 | StopLossAction = stopLossType;
44 | StopLossTimeoutEnabled = stopLossTimeoutEnabled;
45 | StopLossTimeout = stopLossTimeoutInSeconds;
46 | SkipExistingPairs = checkForExistingBots;
47 | QuoteCurrency = quoteCurrency;
48 | Strategy = strategy;
49 | StartOrderType = startOrderType;
50 | MaxSafetyTradesCount = maxSafetyOrders;
51 | MaxActiveSafetyTradesCount = activeSafetyOrdersCount;
52 | PriceDeviationToOpenSafetyOrders = safetyOrderStepPercentage;
53 | SafetyOrderVolumeScale = martingaleVolumeCoefficient;
54 | SafetyOrderStepScale = martingaleStepCoefficient;
55 | TargetProfit = takeProfitPercentage;
56 | TrailingEnabled = trailingEnabled;
57 | TrailingDeviation = trailingDeviation;
58 | Botname = nameFormula;
59 | BaseOrderSize = baseOrderVolume;
60 | SafetyOrderSize = safetyOrderVolume;
61 | DealStartConditions = dealStartConditions;
62 | CooldownBetweenDeals = cooldownBetweenDeals;
63 | ExchangeAccountId = accountId;
64 | BaseCurrencyToBuy = amountToBuyInQuoteCurrency;
65 | SkipBlacklistedPairs = checkForBlacklistedPairs;
66 | SkipBaseStablecoin = checkForBaseStablecoin;
67 | }
68 |
69 | public string Name { get; set; }
70 |
71 | public LeverageType LeverageType { get; set; }
72 |
73 | public int LeverageCustomValue { get; set; }
74 |
75 | public bool SkipExistingPairs { get; set; }
76 |
77 | public bool SkipBlacklistedPairs { get; set; }
78 |
79 | public bool SkipBaseStablecoin { get; set; }
80 |
81 | public string QuoteCurrency { get; set; }
82 |
83 | public string Botname { get; set; }
84 |
85 | public int? ExchangeAccountId { get; set; }
86 |
87 | public Strategy Strategy { get; set; }
88 |
89 | public ProfitCurrency ProfitCurrency { get; set; }
90 |
91 | public decimal BaseOrderSize { get; set; }
92 |
93 | public VolumeType BaseOrderSizeType { get; set; }
94 |
95 | public decimal SafetyOrderSize { get; set; }
96 |
97 | public VolumeType SafetyOrderSizeType { get; set; }
98 |
99 | public StartOrderType StartOrderType { get; set; }
100 |
101 | public decimal TargetProfit { get; set; }
102 |
103 | public string TargetProfitType { get; set; }
104 |
105 | public bool TrailingEnabled { get; set; }
106 |
107 | public decimal TrailingDeviation { get; set; }
108 |
109 | public List DealStartConditions { get; set; } = new List();
110 |
111 | public bool StopLossEnabled { get; set; }
112 |
113 | public decimal StopLossPercentage { get; set; }
114 |
115 | public StopLossType StopLossAction { get; set; }
116 |
117 | public bool StopLossTimeoutEnabled { get; set; }
118 |
119 | public int StopLossTimeout { get; set; }
120 |
121 | public int MaxSafetyTradesCount { get; set; }
122 |
123 | public int MaxActiveSafetyTradesCount { get; set; }
124 |
125 | public decimal PriceDeviationToOpenSafetyOrders { get; set; }
126 |
127 | public decimal SafetyOrderVolumeScale { get; set; }
128 |
129 | public decimal SafetyOrderStepScale { get; set; }
130 |
131 | public decimal DontStartDealIfDailyVolumeIsLessThan { get; set; }
132 |
133 | public decimal? MinimumPriceToOpenDeal { get; set; }
134 |
135 | public decimal? MaxPriceToOpenDeal { get; set; }
136 |
137 | public int CooldownBetweenDeals { get; set; }
138 |
139 | public int? OpenDealsAndStopCount { get; set; }
140 |
141 | public bool BuyBaseCurrency { get; set; }
142 |
143 | public decimal BaseCurrencyToBuy { get; set; }
144 | }
145 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/MainForm/IMainForm.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using _3Commas.BotCreator.Infrastructure;
3 | using _3Commas.BotCreator.Services.BotSettingService;
4 | using XCommas.Net.Objects;
5 |
6 | namespace _3Commas.BotCreator.Views.MainForm
7 | {
8 | public interface IMainForm : IViewBase
9 | {
10 | void BindCombos();
11 | bool IsBinanceSelected { get; }
12 | bool IsHuobiSelected { get; }
13 | int StartConditionsCount { get; }
14 | int NumberOfBotsToCreate { get; }
15 | bool Enable { get; }
16 | List SelectedStartConditions { get;}
17 | BotSettings SelectedTemplate { get; }
18 | bool UsePaperTrading { get; }
19 | void SetNamePreview(string name);
20 | void BindAccountsAndSetSelection(List accounts, int? account);
21 | void ClearLog();
22 | void RefreshQuoteCurrencyLabel(string text);
23 | void SetControlStateToBuyBaseCurrency(bool isEnabled);
24 | void SetCreateInProgress(bool inProgress);
25 | void RefreshStartConditions(List startConditions);
26 | void RefreshTemplateCombo(List templates, bool selectFirst);
27 | void BindSetting(BotSettingViewModel botSetting);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/MainForm/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 | using _3Commas.BotCreator.Misc;
6 | using _3Commas.BotCreator.Services.BotSettingService;
7 | using _3Commas.BotCreator.Services.MessageBoxService;
8 | using AutoMapper;
9 | using XCommas.Net.Objects;
10 |
11 | namespace _3Commas.BotCreator.Views.MainForm
12 | {
13 | public partial class MainForm : Form, IMainForm
14 | {
15 | private readonly MainFormPresenter _presenter;
16 |
17 | public MainForm(IMessageBoxService mbs, IBotSettingService bss, IMapper mapper)
18 | {
19 | InitializeComponent();
20 |
21 | this.Text = $"{AssemblyHelper.AssemblyTitle} {AssemblyHelper.AssemblyVersion}";
22 |
23 | _presenter = new MainFormPresenter(this, new TextBoxLogger(txtOutput), mbs, bss, mapper);
24 |
25 | panelStopLoss.DataBindings.Add(nameof(Enabled), chkStopLossEnabled, nameof(CheckBox.Checked));
26 | lblStopLossTimeoutUnit.DataBindings.Add(nameof(Enabled), chkStopLossTimeoutEnabled, nameof(CheckBox.Checked));
27 |
28 |
29 | }
30 |
31 | private async void MainForm_Load(object sender, EventArgs e)
32 | {
33 | await _presenter.OnViewReady();
34 | }
35 |
36 | public void BindCombos()
37 | {
38 | ControlHelper.AddValuesToCombobox(cmbStrategy, Strategy.Long);
39 | ControlHelper.AddValuesToCombobox(cmbStartOrderType);
40 | ControlHelper.AddValuesToCombobox(cmbStopLossType);
41 | ControlHelper.AddValuesToCombobox(cmbLeverageType);
42 | }
43 |
44 | private async void btnCreate_Click(object sender, EventArgs e)
45 | {
46 | await _presenter.OnCreate();
47 | }
48 |
49 | private void btnClear_Click(object sender, EventArgs e)
50 | {
51 | _presenter.OnClearClick();
52 | }
53 |
54 | private void txtQuoteCurrency_TextChanged(object sender, EventArgs e)
55 | {
56 | _presenter.OnQuoteCurrencyChanged();
57 | }
58 |
59 | private void cmbStrategy_SelectedIndexChanged(object sender, EventArgs e)
60 | {
61 | if (cmbStrategy.DataBindings.Count > 0) cmbStrategy.DataBindings[0].WriteValue();
62 | _presenter.OnStrategyChanged();
63 | }
64 |
65 | private void chkTrailing_CheckedChanged(object sender, EventArgs e)
66 | {
67 | lblTrailing.Enabled = chkTrailing.Checked;
68 | numTrailingDeviation.Enabled = chkTrailing.Checked;
69 | lblTrailingUnit.Enabled = chkTrailing.Checked;
70 | }
71 |
72 | private void btnAbout_Click(object sender, EventArgs e)
73 | {
74 | _presenter.OnAboutClicked();
75 | }
76 |
77 | private void btnAddStartCondition_Click(object sender, EventArgs e)
78 | {
79 | _presenter.OnAddStartConditionClicked();
80 | }
81 |
82 | private void btnRemove_Click(object sender, EventArgs e)
83 | {
84 | _presenter.OnRemoveStartCondition();
85 | }
86 |
87 | private void txtBotname_TextChanged(object sender, EventArgs e)
88 | {
89 | if (txtBotname.DataBindings.Count > 0) txtBotname.DataBindings[0].WriteValue();
90 | _presenter.OnBotNameChanged();
91 | }
92 |
93 | private async void linkLabel3Commas_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
94 | {
95 | await _presenter.On3CommasLinkClicked();
96 | }
97 |
98 | private void linkLabelBinance_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
99 | {
100 | _presenter.OnBinanceLinkClicked();
101 | }
102 |
103 | private void linkLabelHuobi_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
104 | {
105 | _presenter.OnHuobiLinkClicked();
106 | }
107 |
108 | private void chkBuyBase_CheckedChanged(object sender, EventArgs e)
109 | {
110 | _presenter.OnBuyBaseChanged(chkBuyBase.Checked);
111 | }
112 |
113 | public List SelectedStartConditions => listViewStartConditions.SelectedItems.Cast().Select(selectedItem => (BotStrategy)selectedItem.Tag).ToList();
114 | public BotSettings SelectedTemplate => (BotSettings)cmbTemplates.SelectedItem;
115 | public bool UsePaperTrading => chkUsePaperTrading.Checked;
116 | public bool IsBinanceSelected => rbBinance.Checked;
117 | public bool IsHuobiSelected => rbHuobi.Checked;
118 | public int StartConditionsCount => listViewStartConditions.Items.Count;
119 | public int NumberOfBotsToCreate => (int)numAmount.Value;
120 | public bool Enable => chkEnable.Checked;
121 |
122 | public void SetNamePreview(string name)
123 | {
124 | lblBotNamePreview.Text = name;
125 | }
126 |
127 | public void BindAccountsAndSetSelection(List accounts, int? selectedAccount)
128 | {
129 | cmbExchange.DataSource = accounts;
130 | cmbExchange.ValueMember = nameof(Account.Id);
131 | cmbExchange.DisplayMember = nameof(Account.Name);
132 | cmbExchange.SelectedItem = selectedAccount;
133 | }
134 |
135 | public void ClearLog()
136 | {
137 | txtOutput.Clear();
138 | }
139 |
140 | public void RefreshQuoteCurrencyLabel(string text)
141 | {
142 | lblQuoteCurrency.Text = text;
143 | lblQuoteCurrency.Visible = true;
144 | }
145 |
146 | public void SetControlStateToBuyBaseCurrency(bool isEnabled)
147 | {
148 | lblBuyTitle.Enabled = isEnabled;
149 | lblQuoteCurrency.Enabled = isEnabled;
150 | numAmountToBuy.Enabled = isEnabled;
151 | }
152 |
153 | public void SetCreateInProgress(bool inProgress)
154 | {
155 | panelMain.Enabled = !inProgress;
156 | groupBoxSettings.Enabled = !inProgress;
157 | groupBoxCredentials.Enabled = !inProgress;
158 | progressBar.Visible = inProgress;
159 | btnCancel.Text = "Cancel";
160 | btnCancel.Enabled = inProgress;
161 | btnCancel.Visible = inProgress;
162 | }
163 |
164 | public void RefreshStartConditions(List startConditions)
165 | {
166 | listViewStartConditions.Clear();
167 | foreach (var startCondition in startConditions)
168 | {
169 | listViewStartConditions.Items.Add(new ListViewItem { Tag = startCondition, Text = startCondition.Name, Name = startCondition.GetHashCode().ToString() });
170 | }
171 | }
172 |
173 | public void RefreshTemplateCombo(List templates, bool selectFirst)
174 | {
175 | cmbTemplates.SelectedIndexChanged -= cmbTemplates_SelectedIndexChanged;
176 | cmbTemplates.DataSource = templates;
177 | cmbTemplates.ValueMember = nameof(BotSettings.Id);
178 | cmbTemplates.DisplayMember = nameof(BotSettings.Name);
179 | cmbTemplates.SelectedIndex = selectFirst && templates.Any() ? 0 : -1;
180 | btnDeleteTemplate.Enabled = selectFirst;
181 | cmbTemplates.SelectedIndexChanged += cmbTemplates_SelectedIndexChanged;
182 | }
183 |
184 | public void BindSetting(BotSettingViewModel botSetting)
185 | {
186 | // Accounts will not be bound due to lack of nullable support
187 | if (botSetting.ExchangeAccountId.HasValue && cmbExchange.Items.Count > 0)
188 | {
189 | cmbExchange.SelectedItem = cmbExchange.Items.Cast().Single(x => x.Id == botSetting.ExchangeAccountId);
190 | }
191 | else
192 | {
193 | cmbExchange.SelectedItem = null;
194 | }
195 |
196 | // Deal Start Conditions will not be bound
197 | RefreshStartConditions(botSetting.DealStartConditions);
198 |
199 | #region Regular DataBinding by code
200 |
201 | chkCheckForExistingBots.DataBindings.Clear();
202 | chkCheckForExistingBots.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.SkipExistingPairs));
203 |
204 | chkSkipBlacklistedPairs.DataBindings.Clear();
205 | chkSkipBlacklistedPairs.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.SkipBlacklistedPairs));
206 |
207 | chkCheckForBaseStablecoin.DataBindings.Clear();
208 | chkCheckForBaseStablecoin.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.SkipBaseStablecoin));
209 |
210 | txtQuoteCurrency.DataBindings.Clear();
211 | txtQuoteCurrency.DataBindings.Add(nameof(TextBox.Text), botSetting, nameof(BotSettingViewModel.QuoteCurrency));
212 |
213 | cmbStrategy.SelectedIndexChanged -= cmbStrategy_SelectedIndexChanged;
214 | cmbStrategy.DataBindings.Clear();
215 | cmbStrategy.DataBindings.Add(nameof(ComboBox.SelectedItem), botSetting, nameof(BotSettingViewModel.Strategy), false, DataSourceUpdateMode.OnPropertyChanged);
216 | cmbStrategy.SelectedIndexChanged += cmbStrategy_SelectedIndexChanged;
217 |
218 | txtBotname.DataBindings.Clear();
219 | txtBotname.DataBindings.Add(nameof(TextBox.Text), botSetting, nameof(BotSettingViewModel.Botname), true, DataSourceUpdateMode.OnPropertyChanged);
220 |
221 | cmbStartOrderType.DataBindings.Clear();
222 | cmbStartOrderType.DataBindings.Add(nameof(ComboBox.SelectedItem), botSetting, nameof(BotSettingViewModel.StartOrderType));
223 |
224 | numBaseOrderVolume.DataBindings.Clear();
225 | numBaseOrderVolume.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.BaseOrderSize), false, DataSourceUpdateMode.OnPropertyChanged);
226 |
227 | numSafetyOrderVolume.DataBindings.Clear();
228 | numSafetyOrderVolume.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.SafetyOrderSize), false, DataSourceUpdateMode.OnPropertyChanged);
229 |
230 | numTargetProfit.DataBindings.Clear();
231 | numTargetProfit.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.TargetProfit), false, DataSourceUpdateMode.OnPropertyChanged);
232 |
233 | chkTrailing.DataBindings.Clear();
234 | chkTrailing.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.TrailingEnabled));
235 |
236 | numTrailingDeviation.DataBindings.Clear();
237 | numTrailingDeviation.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.TrailingDeviation), false, DataSourceUpdateMode.OnPropertyChanged);
238 |
239 | numMaxSafetyTradesCount.DataBindings.Clear();
240 | numMaxSafetyTradesCount.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.MaxSafetyTradesCount), false, DataSourceUpdateMode.OnPropertyChanged);
241 |
242 | numMaxActiveSafetyTradesCount.DataBindings.Clear();
243 | numMaxActiveSafetyTradesCount.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.MaxActiveSafetyTradesCount), false, DataSourceUpdateMode.OnPropertyChanged);
244 |
245 | numPriceDeviationToOpenSafetyOrders.DataBindings.Clear();
246 | numPriceDeviationToOpenSafetyOrders.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.PriceDeviationToOpenSafetyOrders), false, DataSourceUpdateMode.OnPropertyChanged);
247 |
248 | numSafetyOrderVolumeScale.DataBindings.Clear();
249 | numSafetyOrderVolumeScale.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.SafetyOrderVolumeScale), false, DataSourceUpdateMode.OnPropertyChanged);
250 |
251 | numSafetyOrderStepScale.DataBindings.Clear();
252 | numSafetyOrderStepScale.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.SafetyOrderStepScale), false, DataSourceUpdateMode.OnPropertyChanged);
253 |
254 | numCooldownBetweenDeals.DataBindings.Clear();
255 | numCooldownBetweenDeals.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.CooldownBetweenDeals), false, DataSourceUpdateMode.OnPropertyChanged);
256 |
257 | chkBuyBase.DataBindings.Clear();
258 | chkBuyBase.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.BuyBaseCurrency));
259 |
260 | numAmountToBuy.DataBindings.Clear();
261 | numAmountToBuy.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.BaseCurrencyToBuy), false, DataSourceUpdateMode.OnPropertyChanged);
262 |
263 | chkStopLossEnabled.DataBindings.Clear();
264 | chkStopLossEnabled.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.StopLossEnabled));
265 |
266 | numStopLossPercentage.DataBindings.Clear();
267 | numStopLossPercentage.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.StopLossPercentage), false, DataSourceUpdateMode.OnPropertyChanged);
268 |
269 | cmbStopLossType.DataBindings.Clear();
270 | cmbStopLossType.DataBindings.Add(nameof(ComboBox.SelectedItem), botSetting, nameof(BotSettingViewModel.StopLossAction));
271 |
272 | chkStopLossTimeoutEnabled.DataBindings.Clear();
273 | chkStopLossTimeoutEnabled.DataBindings.Add(nameof(CheckBox.Checked), botSetting, nameof(BotSettingViewModel.StopLossTimeoutEnabled));
274 |
275 | numStopLossTimeout.DataBindings.Clear();
276 | numStopLossTimeout.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.StopLossTimeout), false, DataSourceUpdateMode.OnPropertyChanged);
277 |
278 | cmbLeverageType.DataBindings.Clear();
279 | cmbLeverageType.DataBindings.Add(nameof(ComboBox.SelectedItem), botSetting, nameof(BotSettingViewModel.LeverageType));
280 |
281 | numCustomLeverageValue.DataBindings.Clear();
282 | numCustomLeverageValue.DataBindings.Add(nameof(NumericUpDown.Value), botSetting, nameof(BotSettingViewModel.LeverageCustomValue), false, DataSourceUpdateMode.OnPropertyChanged);
283 |
284 | #endregion
285 | }
286 |
287 | private void cmbTemplates_SelectedIndexChanged(object sender, EventArgs e)
288 | {
289 | var selectedTemplate = cmbTemplates.SelectedItem as BotSettings;
290 | btnDeleteTemplate.Enabled = selectedTemplate != null;
291 | _presenter.OnTemplateChange(selectedTemplate);
292 | }
293 |
294 | private void cmbExchange_SelectedIndexChanged(object sender, EventArgs e)
295 | {
296 | _presenter.OnAccountChanged(cmbExchange.SelectedItem as Account);
297 | }
298 |
299 | private void btnSave_Click(object sender, EventArgs e)
300 | {
301 | _presenter.OnSave();
302 | }
303 |
304 | private void btnDeleteTemplate_Click(object sender, EventArgs e)
305 | {
306 | _presenter.OnDeleteTemplate();
307 | }
308 |
309 | private void chkStopLossTimeoutEnabled_CheckedChanged(object sender, EventArgs e)
310 | {
311 | numStopLossTimeout.Enabled = chkStopLossTimeoutEnabled.Checked;
312 | }
313 |
314 | private void cmbLeverageType_SelectedIndexChanged(object sender, EventArgs e)
315 | {
316 | var isLeverageNotSpecified = (LeverageType)cmbLeverageType.SelectedItem == LeverageType.NotSpecified;
317 | lblLeverageCustomValue.Enabled = !isLeverageNotSpecified;
318 | numCustomLeverageValue.Enabled = !isLeverageNotSpecified;
319 | }
320 |
321 | private async void btnPreview_Click(object sender, EventArgs e)
322 | {
323 | await _presenter.OnPreview();
324 | }
325 |
326 | private void btnCancel_Click(object sender, EventArgs e)
327 | {
328 | btnCancel.Enabled = false;
329 | btnCancel.Text = "Cancelled!";
330 | _presenter.OnCancelOperation();
331 | }
332 | }
333 | }
334 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/MainForm/MainFormPresenter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using System.Windows.Forms;
7 | using _3Commas.BotCreator._3CommasLayer.Implementations;
8 | using _3Commas.BotCreator.ExchangeLayer;
9 | using _3Commas.BotCreator.Infrastructure;
10 | using _3Commas.BotCreator.Misc;
11 | using _3Commas.BotCreator.Services.BotSettingService;
12 | using _3Commas.BotCreator.Services.MessageBoxService;
13 | using AutoMapper;
14 | using Microsoft.Extensions.DependencyInjection;
15 | using Microsoft.Extensions.Logging;
16 | using XCommas.Net.Objects;
17 |
18 | namespace _3Commas.BotCreator.Views.MainForm
19 | {
20 | public class MainFormPresenter : PresenterBase
21 | {
22 | private readonly Keys _keys = new Keys();
23 | private readonly ILogger _logger;
24 | private readonly IMessageBoxService _mbs;
25 | private readonly IBotSettingService _bss;
26 | private readonly IMapper _mapper;
27 | private List _accounts = new List();
28 | private BotSettingViewModel _settings = new BotSettingViewModel();
29 | private CancellationToken _cancellationToken;
30 | private CancellationTokenSource _cancellationTokenSource;
31 |
32 | public MainFormPresenter(IMainForm view, ILogger logger, IMessageBoxService mbs, IBotSettingService bss, IMapper mapper) : base(view)
33 | {
34 | _logger = logger;
35 | _mbs = mbs;
36 | _bss = bss;
37 | _mapper = mapper;
38 | _keys.ApiKey3Commas = Properties.Settings.Default.ApiKey3Commas;
39 | _keys.Secret3Commas = Properties.Settings.Default.Secret3Commas;
40 | _keys.ApiKeyBinance = Properties.Settings.Default.ApiKeyBinance;
41 | _keys.SecretBinance = Properties.Settings.Default.SecretBinance;
42 | _keys.ApiKeyHuobi = Properties.Settings.Default.ApiKeyHuobi;
43 | _keys.SecretHuobi = Properties.Settings.Default.SecretHuobi;
44 | }
45 |
46 | internal async Task OnViewReady()
47 | {
48 | View.BindCombos();
49 | await RefreshExchangesAndBlacklist();
50 | RefreshTemplates(false);
51 | View.BindSetting(_settings);
52 | RefreshNamePreview();
53 | }
54 |
55 | private void RefreshTemplates(bool selectFirst)
56 | {
57 | var templates = _bss.GetAll();
58 | View.RefreshTemplateCombo(templates, selectFirst);
59 | }
60 |
61 | public void OnBotNameChanged()
62 | {
63 | RefreshNamePreview();
64 | }
65 |
66 | private void RefreshNamePreview()
67 | {
68 | var quoteCurrency = (!String.IsNullOrWhiteSpace(_settings.QuoteCurrency)) ? _settings.QuoteCurrency + "_XXX" : "";
69 | var strategy = _settings.Strategy;
70 |
71 | var namePreview = NameHelper.GenerateBotName(_settings.Botname, quoteCurrency, strategy);
72 | View.SetNamePreview(namePreview);
73 | }
74 |
75 | public void OnHuobiLinkClicked()
76 | {
77 | var settingsPersisted = !string.IsNullOrWhiteSpace(Properties.Settings.Default.ApiKeyHuobi);
78 | var settings = new SetApiKeyView.SetApiKeyView(settingsPersisted, "Huobi API Credentials", "Permissions Needed: Read-Only, Trade", _keys.ApiKeyHuobi, _keys.SecretHuobi);
79 | var dr = settings.ShowDialog();
80 | if (dr == DialogResult.OK)
81 | {
82 | _keys.ApiKeyHuobi = settings.ApiKey;
83 | _keys.SecretHuobi = settings.Secret;
84 |
85 | Properties.Settings.Default.ApiKeyHuobi = settings.PersistKeys ? settings.ApiKey : "";
86 | Properties.Settings.Default.SecretHuobi = settings.PersistKeys ? settings.Secret : "";
87 | Properties.Settings.Default.Save();
88 | }
89 | }
90 |
91 | public void OnBinanceLinkClicked()
92 | {
93 | var settingsPersisted = !string.IsNullOrWhiteSpace(Properties.Settings.Default.ApiKeyBinance);
94 | var settings = new SetApiKeyView.SetApiKeyView(settingsPersisted, "Binance API Credentials", "Permissions Needed: Can Read, Enable Spot Trading", _keys.ApiKeyBinance, _keys.SecretBinance);
95 | var dr = settings.ShowDialog();
96 | if (dr == DialogResult.OK)
97 | {
98 | _keys.ApiKeyBinance = settings.ApiKey;
99 | _keys.SecretBinance = settings.Secret;
100 |
101 | Properties.Settings.Default.ApiKeyBinance = settings.PersistKeys ? settings.ApiKey : "";
102 | Properties.Settings.Default.SecretBinance = settings.PersistKeys ? settings.Secret : "";
103 | Properties.Settings.Default.Save();
104 | }
105 | }
106 |
107 | public async Task On3CommasLinkClicked()
108 | {
109 | var settingsPersisted = !string.IsNullOrWhiteSpace(Properties.Settings.Default.ApiKey3Commas);
110 | var settings = new SetApiKeyView.SetApiKeyView(settingsPersisted, "3Commas API Credentials", "Permissions Needed: BotsRead, BotsWrite, AccountsRead", _keys.ApiKey3Commas, _keys.Secret3Commas);
111 | var dr = settings.ShowDialog();
112 | if (dr == DialogResult.OK)
113 | {
114 | _keys.ApiKey3Commas = settings.ApiKey;
115 | _keys.Secret3Commas = settings.Secret;
116 |
117 | Properties.Settings.Default.ApiKey3Commas = settings.PersistKeys ? settings.ApiKey : "";
118 | Properties.Settings.Default.Secret3Commas = settings.PersistKeys ? settings.Secret : "";
119 | Properties.Settings.Default.Save();
120 |
121 | await RefreshExchangesAndBlacklist();
122 | }
123 | }
124 |
125 | private async Task RefreshExchangesAndBlacklist()
126 | {
127 | if (!String.IsNullOrWhiteSpace(_keys.Secret3Commas) && !String.IsNullOrWhiteSpace(_keys.ApiKey3Commas))
128 | {
129 | var selection = _settings.ExchangeAccountId;
130 | var botMgr = new BotManager(_logger, new XCommasClient(_keys, View.UsePaperTrading), null);
131 | _accounts = await botMgr.RetrieveAccounts();
132 | View.BindAccountsAndSetSelection(_accounts, selection);
133 | }
134 | else
135 | {
136 | View.BindAccountsAndSetSelection(null, null);
137 | }
138 | }
139 |
140 | private bool IsValid()
141 | {
142 | var errors = new List();
143 | if (_accounts.Count == 0)
144 | {
145 | errors.Add("Please specify 3Commas API Credentials and select your 3Commas Exchange Account.");
146 | }
147 | else if (_settings.ExchangeAccountId == null)
148 | {
149 | errors.Add("3Commas Exchange Account not selected.");
150 | }
151 |
152 | if (_settings.BuyBaseCurrency && View.UsePaperTrading)
153 | {
154 | errors.Add("The option to buy base coins is not allowed in combination with paper trading.");
155 | }
156 |
157 | if (!View.IsBinanceSelected && !View.IsHuobiSelected)
158 | {
159 | errors.Add("Choose your preferred Exchange.");
160 | }
161 |
162 | if (View.IsBinanceSelected && (String.IsNullOrWhiteSpace(_keys.ApiKeyBinance) || String.IsNullOrWhiteSpace(_keys.SecretBinance)))
163 | {
164 | errors.Add("API Credentials for Binance missing");
165 | }
166 |
167 | if (View.IsHuobiSelected && (String.IsNullOrWhiteSpace(_keys.ApiKeyHuobi) || String.IsNullOrWhiteSpace(_keys.SecretHuobi)))
168 | {
169 | errors.Add("API Credentials for Huobi missing");
170 | }
171 |
172 | if (string.IsNullOrWhiteSpace(_settings.Botname)) errors.Add("Bot name missing");
173 | if (string.IsNullOrWhiteSpace(_settings.QuoteCurrency)) errors.Add("Quote Currency missing");
174 | if (View.StartConditionsCount == 0) errors.Add("Deal Start Condition missing");
175 | if (_settings.StopLossEnabled)
176 | {
177 | var maxDeviation = CalculateMaxSoDeviation();
178 | if (_settings.StopLossPercentage <= maxDeviation)
179 | {
180 | errors.Add($"Stop Loss should be below the last safety order ({maxDeviation})");
181 | }
182 | }
183 |
184 | if (errors.Any())
185 | {
186 | _mbs.ShowError(String.Join(Environment.NewLine, errors), "Validation Error");
187 | }
188 |
189 | return !errors.Any();
190 | }
191 |
192 | private decimal CalculateMaxSoDeviation()
193 | {
194 | // todo
195 | return 0;
196 | }
197 |
198 | public void OnClearClick()
199 | {
200 | View.ClearLog();
201 | }
202 |
203 | public void OnQuoteCurrencyChanged()
204 | {
205 | View.RefreshQuoteCurrencyLabel(_settings.QuoteCurrency);
206 | RefreshNamePreview();
207 | }
208 |
209 | public void OnBuyBaseChanged(bool isBuyEnabled)
210 | {
211 | View.SetControlStateToBuyBaseCurrency(isBuyEnabled);
212 | }
213 |
214 | public void OnAddStartConditionClicked()
215 | {
216 | var frm = Program.ServiceProvider.GetRequiredService();
217 | if (frm.ShowDialog(View) == DialogResult.OK)
218 | {
219 | _settings.DealStartConditions.Add(frm.Strategy);
220 | View.RefreshStartConditions(_settings.DealStartConditions);
221 | }
222 | }
223 |
224 | public void OnRemoveStartCondition()
225 | {
226 | foreach (var item in View.SelectedStartConditions)
227 | {
228 | _settings.DealStartConditions.Remove(item);
229 | }
230 | View.RefreshStartConditions(_settings.DealStartConditions);
231 | }
232 |
233 | public async Task OnCreate()
234 | {
235 | if (IsValid())
236 | {
237 | var dr = _mbs.ShowQuestion($"Do you really want to create {View.NumberOfBotsToCreate} Bots now?");
238 | if (dr == DialogResult.Yes)
239 | {
240 | View.SetCreateInProgress(true);
241 |
242 | IExchange exchange = null;
243 | if (View.IsBinanceSelected)
244 | {
245 | exchange = new ExchangeLayer.Implementations.Binance(_keys);
246 | }
247 | else if (View.IsHuobiSelected)
248 | {
249 | exchange = new ExchangeLayer.Implementations.Huobi(_keys);
250 | }
251 |
252 | var botMgr = new BotManager(_logger, new XCommasClient(_keys, View.UsePaperTrading), exchange);
253 |
254 | try
255 | {
256 | _cancellationTokenSource = new CancellationTokenSource();
257 | var cancellationToken = _cancellationTokenSource.Token;
258 | await botMgr.CreateBots(View.NumberOfBotsToCreate, View.Enable, _settings, cancellationToken);
259 | View.SetCreateInProgress(false);
260 | _mbs.ShowInformation("Operation finished! See output section for details.");
261 | }
262 | catch (Exception exception)
263 | {
264 | _logger.LogError(exception.Message);
265 | _mbs.ShowError("Error: " + exception);
266 | }
267 | finally
268 | {
269 | View.SetCreateInProgress(false);
270 | }
271 | }
272 | }
273 | }
274 |
275 | public async Task OnPreview()
276 | {
277 | if (IsValid())
278 | {
279 | View.SetCreateInProgress(true);
280 | _logger.LogInformation("PREVIEW BEGIN");
281 |
282 | IExchange exchange = null;
283 | if (View.IsBinanceSelected)
284 | {
285 | exchange = new ExchangeLayer.Implementations.Binance(_keys);
286 | }
287 | else if (View.IsHuobiSelected)
288 | {
289 | exchange = new ExchangeLayer.Implementations.Huobi(_keys);
290 | }
291 |
292 | var botMgr = new BotManager(_logger, new XCommasClient(_keys, View.UsePaperTrading), exchange);
293 |
294 | try
295 | {
296 | _cancellationTokenSource = new CancellationTokenSource();
297 | var cancellationToken = _cancellationTokenSource.Token;
298 | await botMgr.PreviewBotsCreation(View.NumberOfBotsToCreate, _settings, cancellationToken);
299 | View.SetCreateInProgress(false);
300 | _mbs.ShowInformation("Operation finished! See output section for details.");
301 | }
302 | catch (Exception exception)
303 | {
304 | _logger.LogError(exception.Message);
305 | _mbs.ShowError("Error: " + exception);
306 | }
307 | finally
308 | {
309 | View.SetCreateInProgress(false);
310 | }
311 |
312 | _logger.LogInformation("PREVIEW END");
313 | }
314 | }
315 |
316 | public void OnAccountChanged(Account cmbExchangeSelectedItem)
317 | {
318 | _settings.ExchangeAccountId = cmbExchangeSelectedItem?.Id;
319 | }
320 |
321 | public void OnSave()
322 | {
323 | var frm = Program.ServiceProvider.GetRequiredService();
324 | frm.Init(_settings);
325 | var dr = frm.ShowDialog(View);
326 | if (dr == DialogResult.OK)
327 | {
328 | RefreshTemplates(true);
329 | }
330 | }
331 |
332 | public void OnTemplateChange(BotSettings botSettings)
333 | {
334 | _settings = botSettings == null ? new BotSettingViewModel() : _mapper.Map(botSettings);
335 |
336 | View.BindSetting(_settings);
337 | }
338 |
339 | public void OnDeleteTemplate()
340 | {
341 | var dr = _mbs.ShowQuestion($"Do you really want to delete template '{View.SelectedTemplate.Name}' ?");
342 | if (dr == DialogResult.Yes)
343 | {
344 | _bss.Delete(View.SelectedTemplate.Id);
345 | RefreshTemplates(false);
346 | }
347 | }
348 |
349 | public void OnStrategyChanged()
350 | {
351 | RefreshNamePreview();
352 |
353 | _settings.DealStartConditions.Clear();
354 | View.RefreshStartConditions(_settings.DealStartConditions);
355 | }
356 |
357 | public void OnAboutClicked()
358 | {
359 | var frm = Program.ServiceProvider.GetRequiredService();
360 | frm.ShowDialog(View);
361 | }
362 |
363 | public void OnCancelOperation()
364 | {
365 | _cancellationTokenSource.Cancel();
366 | }
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/SaveTemplateView/SaveTemplateView.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace _3Commas.BotCreator.Views.SaveTemplateView
2 | {
3 | partial class SaveTemplateView
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.button1 = new System.Windows.Forms.Button();
32 | this.okButton = new System.Windows.Forms.Button();
33 | this.txtNewTemplate = new System.Windows.Forms.TextBox();
34 | this.cmbExistingTemplate = new System.Windows.Forms.ComboBox();
35 | this.radioButtonCreateNew = new System.Windows.Forms.RadioButton();
36 | this.radioButtonOverwriteExisting = new System.Windows.Forms.RadioButton();
37 | this.label1 = new System.Windows.Forms.Label();
38 | this.SuspendLayout();
39 | //
40 | // button1
41 | //
42 | this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
43 | this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
44 | this.button1.Location = new System.Drawing.Point(152, 123);
45 | this.button1.Name = "button1";
46 | this.button1.Size = new System.Drawing.Size(75, 23);
47 | this.button1.TabIndex = 28;
48 | this.button1.Text = "&Cancel";
49 | //
50 | // okButton
51 | //
52 | this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
53 | this.okButton.Location = new System.Drawing.Point(233, 123);
54 | this.okButton.Name = "okButton";
55 | this.okButton.Size = new System.Drawing.Size(75, 23);
56 | this.okButton.TabIndex = 27;
57 | this.okButton.Text = "&OK";
58 | this.okButton.Click += new System.EventHandler(this.okButton_Click);
59 | //
60 | // txtNewTemplate
61 | //
62 | this.txtNewTemplate.Location = new System.Drawing.Point(167, 31);
63 | this.txtNewTemplate.Name = "txtNewTemplate";
64 | this.txtNewTemplate.Size = new System.Drawing.Size(120, 20);
65 | this.txtNewTemplate.TabIndex = 30;
66 | this.txtNewTemplate.Visible = false;
67 | //
68 | // cmbExistingTemplate
69 | //
70 | this.cmbExistingTemplate.FormattingEnabled = true;
71 | this.cmbExistingTemplate.Location = new System.Drawing.Point(167, 69);
72 | this.cmbExistingTemplate.Name = "cmbExistingTemplate";
73 | this.cmbExistingTemplate.Size = new System.Drawing.Size(121, 21);
74 | this.cmbExistingTemplate.TabIndex = 31;
75 | this.cmbExistingTemplate.Visible = false;
76 | //
77 | // radioButtonCreateNew
78 | //
79 | this.radioButtonCreateNew.AutoSize = true;
80 | this.radioButtonCreateNew.Location = new System.Drawing.Point(28, 32);
81 | this.radioButtonCreateNew.Name = "radioButtonCreateNew";
82 | this.radioButtonCreateNew.Size = new System.Drawing.Size(122, 17);
83 | this.radioButtonCreateNew.TabIndex = 33;
84 | this.radioButtonCreateNew.TabStop = true;
85 | this.radioButtonCreateNew.Text = "Create new template";
86 | this.radioButtonCreateNew.UseVisualStyleBackColor = true;
87 | this.radioButtonCreateNew.CheckedChanged += new System.EventHandler(this.radioButtonCreateNew_CheckedChanged);
88 | //
89 | // radioButtonOverwriteExisting
90 | //
91 | this.radioButtonOverwriteExisting.AutoSize = true;
92 | this.radioButtonOverwriteExisting.Location = new System.Drawing.Point(28, 70);
93 | this.radioButtonOverwriteExisting.Name = "radioButtonOverwriteExisting";
94 | this.radioButtonOverwriteExisting.Size = new System.Drawing.Size(108, 17);
95 | this.radioButtonOverwriteExisting.TabIndex = 34;
96 | this.radioButtonOverwriteExisting.TabStop = true;
97 | this.radioButtonOverwriteExisting.Text = "Overwrite existing";
98 | this.radioButtonOverwriteExisting.UseVisualStyleBackColor = true;
99 | this.radioButtonOverwriteExisting.CheckedChanged += new System.EventHandler(this.radioButtonCreateNew_CheckedChanged);
100 | //
101 | // label1
102 | //
103 | this.label1.AutoSize = true;
104 | this.label1.Location = new System.Drawing.Point(45, 52);
105 | this.label1.Name = "label1";
106 | this.label1.Size = new System.Drawing.Size(16, 13);
107 | this.label1.TabIndex = 35;
108 | this.label1.Text = "or";
109 | //
110 | // SaveTemplateView
111 | //
112 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
113 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
114 | this.ClientSize = new System.Drawing.Size(320, 158);
115 | this.Controls.Add(this.label1);
116 | this.Controls.Add(this.radioButtonOverwriteExisting);
117 | this.Controls.Add(this.radioButtonCreateNew);
118 | this.Controls.Add(this.txtNewTemplate);
119 | this.Controls.Add(this.cmbExistingTemplate);
120 | this.Controls.Add(this.button1);
121 | this.Controls.Add(this.okButton);
122 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
123 | this.MaximizeBox = false;
124 | this.MinimizeBox = false;
125 | this.Name = "SaveTemplateView";
126 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
127 | this.Text = "Save Template";
128 | this.ResumeLayout(false);
129 | this.PerformLayout();
130 |
131 | }
132 |
133 | #endregion
134 |
135 | private System.Windows.Forms.Button button1;
136 | private System.Windows.Forms.Button okButton;
137 | private System.Windows.Forms.TextBox txtNewTemplate;
138 | private System.Windows.Forms.ComboBox cmbExistingTemplate;
139 | private System.Windows.Forms.RadioButton radioButtonCreateNew;
140 | private System.Windows.Forms.RadioButton radioButtonOverwriteExisting;
141 | private System.Windows.Forms.Label label1;
142 | }
143 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/SaveTemplateView/SaveTemplateView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Windows.Forms;
5 | using _3Commas.BotCreator.Services.BotSettingService;
6 | using _3Commas.BotCreator.Services.MessageBoxService;
7 | using _3Commas.BotCreator.Views.MainForm;
8 | using AutoMapper;
9 |
10 | namespace _3Commas.BotCreator.Views.SaveTemplateView
11 | {
12 | public partial class SaveTemplateView : Form
13 | {
14 | private readonly IBotSettingService _templateService;
15 | private BotSettingViewModel _botSettings;
16 | private readonly IMessageBoxService _mbs = new MessageBoxService();
17 | private readonly IMapper _mapper;
18 |
19 | public SaveTemplateView(IBotSettingService templateService, IMapper mapper)
20 | {
21 | _mapper = mapper;
22 | _templateService = templateService;
23 | InitializeComponent();
24 |
25 | cmbExistingTemplate.DisplayMember = nameof(BotSettings.Name);
26 | cmbExistingTemplate.ValueMember = nameof(BotSettings.Id);
27 | cmbExistingTemplate.DataSource = _templateService.GetAll();
28 | }
29 |
30 | public void Init(BotSettingViewModel settings)
31 | {
32 | _botSettings = settings;
33 | }
34 |
35 | private void radioButtonCreateNew_CheckedChanged(object sender, EventArgs e)
36 | {
37 | txtNewTemplate.Visible = sender == radioButtonCreateNew;
38 | cmbExistingTemplate.Visible = sender == radioButtonOverwriteExisting;
39 | }
40 |
41 | private void okButton_Click(object sender, EventArgs e)
42 | {
43 | if (IsValid())
44 | {
45 | bool result = false;
46 |
47 | var settings = _mapper.Map(_botSettings);
48 |
49 | if (radioButtonCreateNew.Checked)
50 | {
51 | result = _templateService.Insert(settings, txtNewTemplate.Text);
52 | }
53 | if (radioButtonOverwriteExisting.Checked)
54 | {
55 | result = _templateService.Update(settings, (Guid)cmbExistingTemplate.SelectedValue);
56 | }
57 |
58 | if (!result)
59 | {
60 | _mbs.ShowError("An error occured while saving the bot settings");
61 | }
62 | else
63 | {
64 | this.DialogResult = DialogResult.OK;
65 | }
66 | }
67 | }
68 |
69 | private bool IsValid()
70 | {
71 | var errors = new List();
72 |
73 | if (!radioButtonCreateNew.Checked && !radioButtonOverwriteExisting.Checked) errors.Add("Please select an option");
74 | if (radioButtonCreateNew.Checked && string.IsNullOrWhiteSpace(txtNewTemplate.Text)) errors.Add("Please specify a name for the new Template");
75 | if (radioButtonCreateNew.Checked && cmbExistingTemplate.Items.Cast().Any(x => x.Name == txtNewTemplate.Text)) errors.Add("This name is already in use");
76 | if (radioButtonOverwriteExisting.Checked && cmbExistingTemplate.SelectedItem == null) errors.Add("Existing Template not selected");
77 |
78 | if (errors.Any())
79 | {
80 | _mbs.ShowError(String.Join(Environment.NewLine, errors), "Validation Error");
81 | }
82 |
83 | return !errors.Any();
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/SaveTemplateView/SaveTemplateView.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/SetApiKeyView/SetApiKeyView.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace _3Commas.BotCreator.Views.SetApiKeyView
2 | {
3 | partial class SetApiKeyView
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.txtBinanceApiKey = new System.Windows.Forms.TextBox();
32 | this.txtBinanceSecret = new System.Windows.Forms.TextBox();
33 | this.btnApply = new System.Windows.Forms.Button();
34 | this.pictureBox2 = new System.Windows.Forms.PictureBox();
35 | this.label1 = new System.Windows.Forms.Label();
36 | this.lblPermissionsNeeded = new System.Windows.Forms.Label();
37 | this.label2 = new System.Windows.Forms.Label();
38 | this.btnCancel = new System.Windows.Forms.Button();
39 | this.chkPersist = new System.Windows.Forms.CheckBox();
40 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit();
41 | this.SuspendLayout();
42 | //
43 | // txtBinanceApiKey
44 | //
45 | this.txtBinanceApiKey.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
46 | | System.Windows.Forms.AnchorStyles.Right)));
47 | this.txtBinanceApiKey.Location = new System.Drawing.Point(79, 12);
48 | this.txtBinanceApiKey.Name = "txtBinanceApiKey";
49 | this.txtBinanceApiKey.Size = new System.Drawing.Size(483, 20);
50 | this.txtBinanceApiKey.TabIndex = 21;
51 | //
52 | // txtBinanceSecret
53 | //
54 | this.txtBinanceSecret.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
55 | | System.Windows.Forms.AnchorStyles.Right)));
56 | this.txtBinanceSecret.Location = new System.Drawing.Point(79, 38);
57 | this.txtBinanceSecret.Name = "txtBinanceSecret";
58 | this.txtBinanceSecret.Size = new System.Drawing.Size(483, 20);
59 | this.txtBinanceSecret.TabIndex = 22;
60 | //
61 | // btnApply
62 | //
63 | this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
64 | this.btnApply.Location = new System.Drawing.Point(487, 95);
65 | this.btnApply.Name = "btnApply";
66 | this.btnApply.Size = new System.Drawing.Size(75, 23);
67 | this.btnApply.TabIndex = 27;
68 | this.btnApply.Text = "Apply";
69 | this.btnApply.UseVisualStyleBackColor = true;
70 | this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
71 | //
72 | // pictureBox2
73 | //
74 | this.pictureBox2.Image = global::_3Commas.BotCreator.Properties.Resources.Info_16x16;
75 | this.pictureBox2.Location = new System.Drawing.Point(79, 64);
76 | this.pictureBox2.Name = "pictureBox2";
77 | this.pictureBox2.Size = new System.Drawing.Size(16, 16);
78 | this.pictureBox2.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
79 | this.pictureBox2.TabIndex = 28;
80 | this.pictureBox2.TabStop = false;
81 | //
82 | // label1
83 | //
84 | this.label1.AutoSize = true;
85 | this.label1.Location = new System.Drawing.Point(28, 15);
86 | this.label1.Name = "label1";
87 | this.label1.Size = new System.Drawing.Size(45, 13);
88 | this.label1.TabIndex = 23;
89 | this.label1.Text = "API Key";
90 | this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
91 | //
92 | // lblPermissionsNeeded
93 | //
94 | this.lblPermissionsNeeded.AutoSize = true;
95 | this.lblPermissionsNeeded.Location = new System.Drawing.Point(101, 66);
96 | this.lblPermissionsNeeded.Name = "lblPermissionsNeeded";
97 | this.lblPermissionsNeeded.Size = new System.Drawing.Size(115, 13);
98 | this.lblPermissionsNeeded.TabIndex = 27;
99 | this.lblPermissionsNeeded.Text = "";
100 | //
101 | // label2
102 | //
103 | this.label2.AutoSize = true;
104 | this.label2.Location = new System.Drawing.Point(35, 42);
105 | this.label2.Name = "label2";
106 | this.label2.Size = new System.Drawing.Size(38, 13);
107 | this.label2.TabIndex = 24;
108 | this.label2.Text = "Secret";
109 | this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
110 | //
111 | // btnCancel
112 | //
113 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
114 | this.btnCancel.Location = new System.Drawing.Point(406, 95);
115 | this.btnCancel.Name = "btnCancel";
116 | this.btnCancel.Size = new System.Drawing.Size(75, 23);
117 | this.btnCancel.TabIndex = 30;
118 | this.btnCancel.Text = "Cancel";
119 | this.btnCancel.UseVisualStyleBackColor = true;
120 | this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
121 | //
122 | // chkPersist
123 | //
124 | this.chkPersist.AutoSize = true;
125 | this.chkPersist.Location = new System.Drawing.Point(79, 99);
126 | this.chkPersist.Name = "chkPersist";
127 | this.chkPersist.Size = new System.Drawing.Size(98, 17);
128 | this.chkPersist.TabIndex = 31;
129 | this.chkPersist.Text = "Persist Settings";
130 | this.chkPersist.UseVisualStyleBackColor = true;
131 | this.chkPersist.CheckedChanged += new System.EventHandler(this.chkPersist_CheckedChanged);
132 | //
133 | // Settings
134 | //
135 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
136 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
137 | this.ClientSize = new System.Drawing.Size(574, 130);
138 | this.Controls.Add(this.chkPersist);
139 | this.Controls.Add(this.pictureBox2);
140 | this.Controls.Add(this.btnCancel);
141 | this.Controls.Add(this.label1);
142 | this.Controls.Add(this.lblPermissionsNeeded);
143 | this.Controls.Add(this.btnApply);
144 | this.Controls.Add(this.label2);
145 | this.Controls.Add(this.txtBinanceApiKey);
146 | this.Controls.Add(this.txtBinanceSecret);
147 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
148 | this.MaximizeBox = false;
149 | this.MinimizeBox = false;
150 | this.Name = "SetApiKeyView";
151 | this.ShowIcon = false;
152 | this.ShowInTaskbar = false;
153 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
154 | this.Text = "Settings";
155 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit();
156 | this.ResumeLayout(false);
157 | this.PerformLayout();
158 |
159 | }
160 |
161 | #endregion
162 | private System.Windows.Forms.TextBox txtBinanceApiKey;
163 | private System.Windows.Forms.TextBox txtBinanceSecret;
164 | private System.Windows.Forms.Button btnApply;
165 | private System.Windows.Forms.Label label1;
166 | private System.Windows.Forms.Label label2;
167 | private System.Windows.Forms.PictureBox pictureBox2;
168 | private System.Windows.Forms.Label lblPermissionsNeeded;
169 | private System.Windows.Forms.Button btnCancel;
170 | private System.Windows.Forms.CheckBox chkPersist;
171 | }
172 | }
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/SetApiKeyView/SetApiKeyView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Configuration;
3 | using System.Windows.Forms;
4 |
5 | namespace _3Commas.BotCreator.Views.SetApiKeyView
6 | {
7 | public partial class SetApiKeyView : Form
8 | {
9 | private readonly bool _viewLoaded;
10 |
11 | public SetApiKeyView(bool persistSettings, string title, string permissionsNeeded, string apiKey, string secret)
12 | {
13 | InitializeComponent();
14 | chkPersist.Checked = persistSettings;
15 | Text = title;
16 | lblPermissionsNeeded.Text = permissionsNeeded;
17 |
18 | ApiKey = apiKey;
19 | Secret = secret;
20 | txtBinanceApiKey.Text = apiKey;
21 | txtBinanceSecret.Text = secret;
22 |
23 | _viewLoaded = true;
24 | }
25 |
26 | private void btnApply_Click(object sender, EventArgs e)
27 | {
28 | ApiKey = txtBinanceApiKey.Text;
29 | Secret = txtBinanceSecret.Text;
30 |
31 | DialogResult = DialogResult.OK;
32 | }
33 |
34 | public string ApiKey { get; set; }
35 |
36 | public string Secret { get; set; }
37 |
38 | public bool PersistKeys => chkPersist.Checked;
39 |
40 | private void btnCancel_Click(object sender, EventArgs e)
41 | {
42 | this.Close();
43 | }
44 |
45 | private void chkPersist_CheckedChanged(object sender, EventArgs e)
46 | {
47 | if (_viewLoaded && chkPersist.Checked)
48 | {
49 | MessageBox.Show("Your keys will be persisted in your user directory. Be aware that other programs may potentially have access to it. In addition, after an application update, the configuration files of previous versions may still exist. Only an uninstallation will delete all configuration data." + Environment.NewLine + Environment.NewLine +
50 | "Settings location: " + System.IO.Path.GetDirectoryName(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/Views/SetApiKeyView/SetApiKeyView.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/3Commas.BotCreator/robot.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarcDrexler/3Commas.BotCreator/9e2fda03e4d81f2e54452d899901633d087d81ed/src/3Commas.BotCreator/robot.ico
--------------------------------------------------------------------------------