From 65d8cd957f8bc60469fe9a74b154228cab538ffd Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 6 Feb 2026 12:09:26 +0000 Subject: [PATCH] BIT --- .../context_processors.cpython-311.pyc | Bin 731 -> 753 bytes core/__pycache__/models.cpython-311.pyc | Bin 13582 -> 15220 bytes core/__pycache__/urls.cpython-311.pyc | Bin 1949 -> 2281 bytes core/__pycache__/views.cpython-311.pyc | Bin 13814 -> 20870 bytes core/context_processors.py | 5 +- ...price_remove_order_entry_price_and_more.py | 69 ++++ ...order_entry_price_and_more.cpython-311.pyc | Bin 0 -> 3544 bytes core/models.py | 36 ++- core/templates/base.html | 38 +-- core/templates/core/index.html | 8 +- core/templates/core/login.html | 2 +- core/templates/core/market_center.html | 107 +++++++ core/templates/core/profile.html | 6 +- core/templates/core/register.html | 4 +- core/templates/core/trade.html | 268 +++++++++++++--- core/urls.py | 7 + core/views.py | 294 ++++++++++++------ 17 files changed, 673 insertions(+), 171 deletions(-) create mode 100644 core/migrations/0006_remove_order_close_price_remove_order_entry_price_and_more.py create mode 100644 core/migrations/__pycache__/0006_remove_order_close_price_remove_order_entry_price_and_more.cpython-311.pyc create mode 100644 core/templates/core/market_center.html diff --git a/core/__pycache__/context_processors.cpython-311.pyc b/core/__pycache__/context_processors.cpython-311.pyc index 000b2310d9f95e8de073b8acb516d02e9d0fced9..bd209b1fb75962cfd755aea89fa2c2b5ed605530 100644 GIT binary patch delta 156 zcmcc3`jM4)IWI340}yy@Y0ca=kylbi2FRJtkiw9{n8T3E7{$oQkiyi$5XF?r6ve#J z?g*psE&hU{{H)aElKABOypq(4k|Kf0oJ?t!EVtNl^HWlDii?o-leE<<)#Q+7597q5F diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index d3e41d76845bdcf280b89f9871a9f3c0304fe806..782412ce291f3ff201a242acba1f03c0e94dbce8 100644 GIT binary patch delta 3288 zcmah~TU1lo8O})vgv$vel5h zo-*c{=)Bc8KV-%2{esQs_CG)B_nK?3*m(9c|6^9g@{w)Q!?P^zAl#XNS z6}H6b8m3dLELKg=$4@ba1oXqiC2`4Iic-O=xO7^nGwbwFk92MeX+dwIU3RM>QIf`n zrHQyras_WG8q>H!*c|)xjJ}*n z?!i?DFU_pX(4{B0p8a|xGyhoqYYnz(Rbns0l+i)Mf0!fGWqgH6rxV6ujkc9^_VXPM zT9Wi0b7}f%(zh9#A%FO|Xvm*DFxM^A)e9}B@Z}Zk` zHa;qOAp8^|7a*YNBAs14arcoo=}=U1%z4xxR8B8uL@>;G>er+wZ>Z>ed>1{Fc~jXM zK~pVem0`k2_gc*RfF06Cr^q%sRdzgDu+lhL=vYcz5+BJ$U$a~36H9{1#76*$lIV&h z1L_iO7&FbtGMg-17MIOsOGD(yhOpAcEVBnJB<(lx0|}~e>cbP*+OwP`)-g4(1WG71 zB7HSZ<3^jaNh5B@`Rv(3-LFsx7=)Dk>YAh0X<0xc?dBii&S{(};ci3AER-$NzLl8B zONp{YK-prMCXL$(j7{fu0b_U5#+(aAatu1}i33>q`0d~yE-w3qmp;8i|CLjp)=4;? zxJV!9$SVN-ngb073z5z*sLNUy_aYL6%Qqeehp*nH6V`oocy`MlOg_8(e$n#V??COY zitI=4R}oSHdN62&hSeRJKfFgjvg21 zCY>nIwDae9(tMf^M7rF~?j9F$gP8On;B6p<2=OqppGCzb_^^TCfa1ERqrrV1Fu<(o(cJC zFyN@2hVW`Ih^V-vEnQ>J$+A6wM-d*R6u8}~GEEYf2DdwzONZNS3TSFNU2X2R&W=Dt zMRnDo9uFRL@WO||J0Awr_2t!d4L$3UmAjt?qPy0uw_JEa!GnAkp%mdd!Uq77W9YI< z*OYt@HG5Hmkt8_lU7Df6k;_Xn56RCr>P!n4a)8$!4L z=KbLC*z%PN;QWIAuBbJK-B^OXl6NQD@kUe0omg~CTfd#Bq)c0k*O zVJeg8P=!&TKOsE3^>_7sv?>AcDi-y|>jk1dONg)ZKpAbg(*!^GCQBZCCscdnp6v_O z5H*zqV`G&qg;?NEvJjA2q1X9WSi_l;{~z}-mWE;QjOq7z%WM#qf)79XSBxy4cBd0v^Sg=4QV%PA@|;D`bCqFXd6E?Sqr&$ zs_8+Ku%lGicEDS)s5SdqMXh->YI4tm{X$KXpgrl=o)onwy@wXV(dv-}^3likS-Zd< z_OpjY_Amsg*fifBF*a?KOzvHvZ*Dh6#b^1>iSb!fYfg6Rt;4yZ-a4tDD-{}!;dh+# zH}GNuFX&tS`c_fj>OHy`n}J4)FgxC}Q*b`#x3GZu*Y$gWG%<%({ zx)&e|wJrYYQ)2Zgf#v)xC$gNkjP8rKm=<+bjL6o>lDX!&<^|3C**~5Ybaj4Rov5p$ ze@(Gls+d&;qkb7mM@jBYw-tP$%nZMuBSI-aAOfFKGC-S(jz!->>3snDSke@c4FHXzAlaY=#^oT{ddmU$*U1!K$l)LGd#n~|rv5IhQI&bG4CK9VQ zJ@xMzNH~uwAHqF^hxGN`rkFoN6^>frnx#X#AD38|!?3uQqp$T!`*TURw$-AHo-O&7 yCxfW~$Csn8>`KLRNw>Du;`@w_!A*QQ`p#V$d@kwMwp!$Bnc`K&*MR<`B>TVr&Oe3# delta 2155 zcmbVMZA?>F81Ctp6}$-77AV*jT1qX50?L>8F$5F`(=e1NOuNFpOrU_f{fJty#rS~< zL&$Mv36N-_YzxeFHk(tI=#Twy*?!!bE;0SJEWk9&7EOphm%Zmeh26O<*}3;TIq%2w zo{#rEXTdkpmGlY2B*>tZh0b^C3}Z>@DCbL?1*tjIB(t+&AL>g&FL5xx3O~y#48N0H zh3Z-2Pa3ygF5?xPVG7A))6f^&jB!d1GW5mPvJSk|U5Rm-)AA}#MVTgSU`;7nYG013 z$Jfo;0CmWggf>qpAT;!)&^k$I3ztKARtYax!b_#Rw0Injp)A`9RwdYGHb{1oE|=|< zarSdEE>B|7bNT0FEg9TKIJJUs=DIo*_G?cfRD&0EFHs%-EwvnF;!vp$SETvzcWD>U z(Qtiw846G9Zz)v$Fj*r$X80O4W1aDQ;sD43N8k*;Y21if@MB}!7OLGuv^ms7pq@Yv zfk6Vf05Q!8q3;AQcuomtu~?X%;3F0S_2Ek2c9kJx($kGxfZhTTP?=qbl%Tvq@p% zm5^N>%EkdIsj!nJ(jBvD7bK6fa#qUD{rg&GY*Q*oJoLpzYf96#Tr<_rk66_ zv7Dx&>V^YNwKdc*cjxxp-SF&v98>oZzFRm6?m{`hY0d6~)dh!|Y7TmFowZ<%R}ISf zt9R!{$Lx5}x+6g4k3!EbUbZWQorJcFKn4NwLV4E$zy$L{qqFyJ%u`Qc2k4%W_sqKn zV1Dx2{N#nX;D=xps^i+Z^Ec-v?#bAJVB?X!G*YTlQ`Mt|HIro<8W03uL z`(_jVC3gY^aKN?$Wzpy0v8^`e99V@$0-Xew?|B^&#|cc}$_?uePr@+9bac90Jm+xs z^m%(dLNC#2T)dALobU6oL>_df94Ca%b`aWKeQrK3z_ZZN>2ZL{9aFnKPLJ0uxJhsi z{-My2e31+Z9EI-AQ{dYh0!eE}Q{bx{GhQ)@nk-ypFV;U_JQ?@ff3G<5z5hqQ$h1b7)+o~&-19_h8{HGt+Cs$-onqtLqO*4l7ukUbI}l|Dg7r`S zr|Soz<2~{_J0p&+sH00{yCZCOlN% zH^THrnO@iv?#*bxwfR}-VtAtXu{`TlJqlOIA+`{x0*EO|X$q(DNXa49RZw{G@TLx2 zSGr&I6-Xm^ytF!5ntl97X{PZE2|*usn)6kjlaAv~_i^DfqIY9knI&PG3<;bKA1u3p z)MRK+!GD(f6X}aj^Er(#RTvT;fQi(zdkBXr?rt@qedxLD^*Gcq?0P|M3oAW#C$ bB^J~G#Mk4{p5cZU)V8qF<4uGsD=Yp2xN0eX diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index af3b707a070a8222aad6a5fb7493bde64dc9ad72..fa55f1d0a7308c9d7f62e5a3c19dd632b66d398b 100644 GIT binary patch delta 1186 zcmZwGyKmD#7yxj)IVVotG|h`NB_*T~NLt$T0Ys{*NJu;ef*6n(Dv`+Kti+WYTW9m? zk}UiImBonE0l`33RqDe3z>%`$7=dOq@SLYP4`ym8z@$CRJeZJe==k`8C&~btb_!keG+8iV z$1Hf7OJHUkbJ=ZRe0%meKoJLBaiddn)n)Ry01LMW^8bZf8k5&RE_=%M$`~yIUG=1E zV{#qjhNrv^^534g0mij$8}aQZzWInLlm^U0m~(-(kR}?<0Xs~%=d$!dw@_UrlEqk@ z+``gJidy2Xw3jr9h^69|Zjep8Su674eTl%!yMU@_^^ijq9<9`!gONA#o0{xLZ4EPtBqT{Xr delta 884 zcmZwG%TB^T6adgErR7m-DFVJgMUjUnx^n5xFL39^V5Y$mEM|(2UETNrq%p2snP?*2 z@e%$*UAmEt8-Kt%#g1A!O>a8wnLF*B=~#VGYR|HqM9|`oxB6!kp?BKxTkhxL>m@R% z6*>{5aX6~+9dMI|1xI}15gNidX`YFJ5&SzijuSeElOSQ-kd5`E6p$&9vLg>b{WtNLo1<)R>3IDl<*pqbRFb$@yh|Z0kYyt%TM90Q#C+qKM!{U70R&1>L~fHGOQf&PMlq zj&7vi z$d!rK*3G^~ye^@?2Fcs|;)zs&OZrac2WJn(hW#cMM@o$6?1T87Qq3xn8;KsI4CWgI CGLcOH diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 84d71d758cf5e0e2e01ed07ca726fdb9ff2f4f49..7b6c4cf1f0b8880d4739cc7a79b662d3f74f1551 100644 GIT binary patch literal 20870 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 V!41Y*+B3^q**3_{UjS5z{{v66ph*A# diff --git a/core/context_processors.py b/core/context_processors.py index c0cf4a5..b76295e 100644 --- a/core/context_processors.py +++ b/core/context_processors.py @@ -1,6 +1,6 @@ from .models import SiteSettings -def site_settings(request): +def project_context(request): settings = SiteSettings.objects.first() if not settings: settings = SiteSettings.objects.create(site_name="BitCrypto") @@ -9,3 +9,6 @@ def site_settings(request): 'project_name': settings.site_name, 'project_description': "全球领先的数字资产交易平台" } + +# Alias for compatibility if needed +site_settings = project_context \ No newline at end of file diff --git a/core/migrations/0006_remove_order_close_price_remove_order_entry_price_and_more.py b/core/migrations/0006_remove_order_close_price_remove_order_entry_price_and_more.py new file mode 100644 index 0000000..e32ffa9 --- /dev/null +++ b/core/migrations/0006_remove_order_close_price_remove_order_entry_price_and_more.py @@ -0,0 +1,69 @@ +# Generated by Django 5.2.7 on 2026-02-06 11:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_sitesettings'), + ] + + operations = [ + migrations.RemoveField( + model_name='order', + name='close_price', + ), + migrations.RemoveField( + model_name='order', + name='entry_price', + ), + migrations.RemoveField( + model_name='order', + name='filled_amount', + ), + migrations.RemoveField( + model_name='order', + name='profit_loss', + ), + migrations.AddField( + model_name='account', + name='frozen_balance', + field=models.DecimalField(decimal_places=8, default=0, max_digits=30, verbose_name='冻结余额 (USDT)'), + ), + migrations.AddField( + model_name='order', + name='total_usdt', + field=models.DecimalField(blank=True, decimal_places=8, max_digits=30, null=True, verbose_name='成交额 (USDT)'), + ), + migrations.AlterField( + model_name='order', + name='amount', + field=models.DecimalField(decimal_places=8, max_digits=30, verbose_name='数量/手数'), + ), + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('PENDING', '等待成交'), ('PARTIALLY_FILLED', '部分成交'), ('FILLED', '已成交'), ('CANCELED', '已撤销')], default='PENDING', max_length=20, verbose_name='状态'), + ), + migrations.CreateModel( + name='Position', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('symbol', models.CharField(max_length=20, verbose_name='交易对')), + ('side', models.CharField(choices=[('LONG', '做多'), ('SHORT', '做空')], max_length=10, verbose_name='方向')), + ('leverage', models.IntegerField(default=20, verbose_name='杠杆')), + ('entry_price', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='开仓均价')), + ('lots', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='手数')), + ('margin', models.DecimalField(decimal_places=8, max_digits=30, verbose_name='占用保证金')), + ('is_active', models.BooleanField(default=True, verbose_name='是否持仓')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='时间')), + ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='positions', to='core.account')), + ], + options={ + 'verbose_name': '持仓', + 'verbose_name_plural': '持仓管理', + }, + ), + ] diff --git a/core/migrations/__pycache__/0006_remove_order_close_price_remove_order_entry_price_and_more.cpython-311.pyc b/core/migrations/__pycache__/0006_remove_order_close_price_remove_order_entry_price_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9f16c647aaf092622f1fed13366ed747a347aa9 GIT binary patch literal 3544 zcmb7HU2qfE6<)1=EXlGX35;tC%Z30!{MZH;O52py*aqA<4qzqIICgf{y4UiW)vnlG zIf0onX4(Mh&%_C7aWnM{%`lWq)WUQ+i4)S6r;Z=5I{Tp6r_R)}t2Z(DDNjB3t|V-u z8Jd;%=PC(cF6J)M$Ke=n`BPF-6AejKbjy=c z5tj8t8Oy#}%MXulgN&OTW_m|?3EUgQJ4b<^JJiXYXZ0BXoK-f@9-aNAe*xsB0b);^KLqu?evA>;=+Ar#JTxdRdY zv>N4_l6)0C5Sgu;BvCm{*=-y3KT}FGlfo*6%1(0VTOT`63u;A??Dqe|H>fQ8LTxC@ zWCf}#A@Hjmb!2yJlrzbrog1}+w&%0EHs%>nyEmcspcj%n+KXb@eH*<5?0$xAsOoM- z2eJpBlIUir^;ry{&do6cXrh9t-+9Uh zFGB?@C>)v&SNQ3=Dz~M|ZLM(V82WDZdz&Md^rP>i7ts%X&u5Qs+)MgKo6rxTw|`{$ zUas^sG`K6FYS;>OcXuBVbjiRvHVjEg>Xuv85Z0{vgskdVNNZ98vw1*gfS`yeY`GY% z$BLm{v;kJbq$JB231UjkD27#^*3?PK5I}@()rpA&TWy@w)Js?qCPZ0O61Ye+sL_#7 zi~`9}XA4&!tUkE0{I}l~KVInA`|3!4JQj0VjR+^Clqd^nSxjJ%i14JCkqwwq;@bj} zl9HiYff=k#P?M>3F{egrTBBubxhEwoBg=272FS?h$cSfX*WT+|dH0vVfwJm>GE_H2BcsP$5V^WD1H=7; z!>2OTmeoJKTlo7swqn=|p6NXoAM70(dR;g%G&BDUl!>sW6vwU z{h~1UK7{yrD(HzcuZ$t#ViMb^gszZI-d+C3!hi2wDPDWu@=3ZNCJbo?GeIl!_X=}&R(^E_#997w zKm^D!bz|k`XT_U;1vl()De7B-h>-2HFI^6+eknjZuCv?>F{kBI6#;q*8`uhHSf(Mi z`-?YJTP{;)>u2ohpZ~BrN4;eAxMh98Lu+I-QHD6NlJ~HZhfYz`>>WNF+h%zMCSDLM zzaYRXJtJe_1A=fNBg(XC=o7+etRSo;B&Zbn^T4V+lvt6H2Si%0{wPl z+ME7f*}$63`}(HDGF#ur>gQ!TK&-$CDcK7}WupRvih+}rGl3JTDq~Tx_Zs>kB5^5I z*6cqC(UX$OOHg|rG%YGg)#A~F6GchZ{q~mpmn{aYUlMd%#<#go31CndAeXO9u+j^xxfaZux=i1+*mgU1yMl| zT1a#HqDD)D5jxNHqY%v5>Zj!4e0xexeyeA8sO0B-?brRLH=1vb=0nkv+v)Q^;()AC zQu2P2?T&ALdOOt}$v1c8Lp$=}-E(`)@b3F(A3ByMh+vR#CKt|_;Y_K{MXds|MhP{# zauAGes~o)B|1h+qk)Mo_^FJlw%enAnGkm$m*(g=o#vE>`oCT$aNjyQqC>KU%7(MgY zPHOAU62~1#@SA9gpP@^A&H*QU#hqjHYs& z?INQBQ8d!5=bCl1StmgwA8H|y{qsl7$o~7pbCI|iiId=~`A{=yiOp{_TVkYV zfSej7hu=%M*ZGkR!o*V3CLI+lx$nb9#49EYru_MZ6} zv%TlxRuUg0?c=%jakG7#1mDbuTFLf<^C!*i2NykG`OIUd&0}ZHW3Q7b9lW{dI7OXm zJ5AjL3T^_WZVvLLruwaorB-f7EdRn0DAuON5=Ti}n3fliHA+e^IgW)rvp=RE?Odgi zVjio-Zv9R)(P{rz(pl+$Z3tkNue#5s7aLP{C!VEJn5YrpzV^OokHF*FYw+lRTb}?@ raylFi=uXED!gbhxd2TPM^;hy7cX&$N(`3$X26sK88*4RonGOE~NId4x literal 0 HcmV?d00001 diff --git a/core/models.py b/core/models.py index a9c352c..759b4b8 100644 --- a/core/models.py +++ b/core/models.py @@ -57,6 +57,8 @@ class Account(models.Model): account_type = models.CharField(max_length=20, choices=ACCOUNT_TYPES, default='SIMULATED', verbose_name=_("账户类型")) balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("可用余额 (USDT)")) + frozen_balance = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("冻结余额 (USDT)")) + credit_score = models.IntegerField(default=80, verbose_name=_("信用分")) kyc_status = models.CharField(max_length=20, choices=KYC_STATUS, default='UNVERIFIED', verbose_name=_("实名认证状态")) @@ -95,11 +97,10 @@ class Order(models.Model): TYPE_CHOICES = (('LIMIT', _('限价')), ('MARKET', _('市价'))) TRADE_TYPE_CHOICES = (('SPOT', _('现货')), ('CONTRACT', _('合约'))) STATUS_CHOICES = ( - ('LIVE', _('进行中')), + ('PENDING', _('等待成交')), ('PARTIALLY_FILLED', _('部分成交')), ('FILLED', _('已成交')), ('CANCELED', _('已撤销')), - ('CLOSED', _('已平仓')), ) account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='orders') @@ -109,15 +110,11 @@ class Order(models.Model): order_type = models.CharField(max_length=10, choices=TYPE_CHOICES, verbose_name=_("类型")) price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("委托价格")) - amount = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("数量")) - filled_amount = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("已成交数量")) + amount = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("数量/手数")) + total_usdt = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("成交额 (USDT)")) leverage = models.IntegerField(default=1, verbose_name=_("杠杆倍数")) - entry_price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("入场价格")) - close_price = models.DecimalField(max_digits=30, decimal_places=8, null=True, blank=True, verbose_name=_("平仓价格")) - profit_loss = models.DecimalField(max_digits=30, decimal_places=8, default=0, verbose_name=_("盈亏")) - - status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='LIVE', verbose_name=_("状态")) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='PENDING', verbose_name=_("状态")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("创建时间")) class Meta: @@ -127,6 +124,25 @@ class Order(models.Model): def __str__(self): return f"{self.trade_type} {self.side} {self.amount} {self.symbol}" +class Position(models.Model): + SIDE_CHOICES = (('LONG', _('做多')), ('SHORT', _('做空'))) + account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='positions') + symbol = models.CharField(max_length=20, verbose_name=_("交易对")) + side = models.CharField(max_length=10, choices=SIDE_CHOICES, verbose_name=_("方向")) + leverage = models.IntegerField(default=20, verbose_name=_("杠杆")) + entry_price = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("开仓均价")) + lots = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("手数")) + margin = models.DecimalField(max_digits=30, decimal_places=8, verbose_name=_("占用保证金")) + is_active = models.BooleanField(default=True, verbose_name=_("是否持仓")) + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("时间")) + + class Meta: + verbose_name = _("持仓") + verbose_name_plural = _("持仓管理") + + def __str__(self): + return f"{self.account.uid} {self.symbol} {self.side} {self.lots}张" + class Transaction(models.Model): TX_TYPE = (('deposit', _('充值')), ('withdraw', _('提现'))) TX_STATUS = (('pending', _('待处理')), ('completed', _('成功')), ('failed', _('失败'))) @@ -141,4 +157,4 @@ class Transaction(models.Model): class Meta: verbose_name = _("充提记录") - verbose_name_plural = _("充提管理") \ No newline at end of file + verbose_name_plural = _("充提管理") diff --git a/core/templates/base.html b/core/templates/base.html index 43afb9b..b55a06f 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -100,10 +100,10 @@ @@ -149,32 +149,32 @@ diff --git a/core/templates/core/index.html b/core/templates/core/index.html index 3cb6fda..26b3984 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -18,8 +18,8 @@

开启您的
加密货币之旅

在全球最受信任的交易平台买卖和存储加密货币。BitCrypto 为您提供安全、稳定、高效的服务。

@@ -32,7 +32,7 @@

领先的衍生品交易平台

最高200倍杠杆,支持多种永续合约,毫秒级撮合引擎。多空双向交易,灵活捕捉市场机会。

- 进入合约交易 + 进入合约交易
@@ -98,7 +98,7 @@

热门行情

实时获取全球顶级加密货币价格走势

- 查看更多市场 + 查看更多市场
diff --git a/core/templates/core/login.html b/core/templates/core/login.html index 8d6ce2c..7798100 100644 --- a/core/templates/core/login.html +++ b/core/templates/core/login.html @@ -41,7 +41,7 @@
-

还没有账户? 立即注册

+

还没有账户? 立即注册

diff --git a/core/templates/core/market_center.html b/core/templates/core/market_center.html new file mode 100644 index 0000000..d41f222 --- /dev/null +++ b/core/templates/core/market_center.html @@ -0,0 +1,107 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
+
+

行情中心

+
+ + +
+
+ +
+
+ + + + + + + + + + + + + + {% for crypto in cryptos %} + + + + + + + + + + {% endfor %} + +
币种最新价24h 涨跌24h 最高24h 最低24h 成交额操作
+
+ +
+
{{ crypto.symbol }}
+
{{ crypto.name }}
+
+
+
---------- + 现货 + 合约 +
+
+
+
+ + +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/core/templates/core/profile.html b/core/templates/core/profile.html index 437e3ee..f472f2a 100644 --- a/core/templates/core/profile.html +++ b/core/templates/core/profile.html @@ -23,8 +23,8 @@
@@ -122,7 +122,7 @@
身份认证 (KYC)

提升提现额度至 100 BTC

- 立即认证 + 立即认证 diff --git a/core/templates/core/register.html b/core/templates/core/register.html index 2aaa8be..6c189de 100644 --- a/core/templates/core/register.html +++ b/core/templates/core/register.html @@ -66,7 +66,7 @@
@@ -75,7 +75,7 @@
-

已有账户? 立即登录

+

已有账户? 立即登录

diff --git a/core/templates/core/trade.html b/core/templates/core/trade.html index 912103a..390bc34 100644 --- a/core/templates/core/trade.html +++ b/core/templates/core/trade.html @@ -57,8 +57,8 @@ @@ -68,7 +68,7 @@ -
+
{% if trade_type == 'SPOT' %}
@@ -87,9 +87,9 @@
- 成交额 - - USDT + 数量 + + {{ base_symbol }}
@@ -103,7 +103,7 @@ 可用余额 {{ account.balance|default:"0.00" }} USDT
- +
@@ -124,7 +124,7 @@
数量 - {{ symbol|slice:":-4" }} + {{ base_symbol }}
@@ -135,10 +135,10 @@
- 可用 {{ symbol|slice:":-4" }} - 0.00 + 可用 {{ base_symbol }} + {{ base_asset_balance|default:"0.00" }} {{ base_symbol }}
- +
{% else %} @@ -147,9 +147,9 @@
- + - +
手数 - +
-
+
+
+ 0%25%50%75%100% +
- 保证金: 0.00 USDT + 所需保证金: 0.00 USDT 可用: {{ account.balance|default:"0.00" }} USDT
-
-
+
+
-
合约面值100 USDT
+
合约面值100 USDT / 张
当前杠杆20x
预计手续费0.05%
-
维持保证金0.4%
+
维持保证金率0.4%
{% endif %} + + +
+ +
+
+
+ + + + + + + + + + + + + + + {% if trade_type == 'CONTRACT' %} + {% for p in account.positions.all %} + {% if p.is_active %} + + + + + + + + + + + {% endif %} + {% endfor %} + {% endif %} + {% for o in account.orders.all %} + {% if o.status == 'PENDING' %} + + + + + + + + + + + {% endif %} + {% endfor %} + +
合约/现货方向数量/手数开仓/委托价当前价保证金盈亏操作
{{ p.symbol }} {{ p.leverage }}x{{ p.get_side_display }}{{ p.lots }}{{ p.entry_price }}--{{ p.margin|floatformat:2 }}--
{{ o.symbol }} ({{ o.get_trade_type_display }}){{ o.get_side_display }}{{ o.amount }}{{ o.price|default:"市价" }}----等待成交
+
+
+
+
@@ -202,7 +266,7 @@
价格(USDT) - 数量({{ symbol|slice:":-4" }}) + 数量({{ base_symbol }})
@@ -216,7 +280,7 @@ {% endblock %} @@ -234,6 +299,9 @@ const symbol = '{{ symbol }}'; const tradeType = '{{ trade_type }}'; const balance = parseFloat("{{ account.balance|default:0 }}"); + const baseAsset = '{{ base_symbol }}'; + const assetBalance = parseFloat("{{ base_asset_balance|default:0 }}"); + let currentPrice = 0; let leverage = 20; let allCoins = []; @@ -250,7 +318,7 @@ try { const r = await fetch('https://api.binance.com/api/v3/ticker/24hr'); const data = await r.json(); - allCoins = data.filter(c => c.symbol.endsWith('USDT')).slice(0, 50); + allCoins = data.filter(c => c.symbol.endsWith('USDT')).slice(0, 100); renderCoins(); } catch(e) {} } @@ -264,14 +332,15 @@ if (filter && !c.symbol.toLowerCase().includes(filter.toLowerCase())) return; const base = c.symbol.replace('USDT', ''); const chg = parseFloat(c.priceChangePercent); + const iconUrl = `https://static.okx.com/cdn/oksupport/asset/currency/icon/${base.toLowerCase()}.png`; const row = ` - ${base} - ${parseFloat(c.lastPrice).toFixed(2)} + ${base} + ${parseFloat(c.lastPrice).toLocaleString()} ${chg>=0?'+':''}${chg.toFixed(2)}% `; list.innerHTML += row; - mobileList.innerHTML += `
  • ${base}${parseFloat(c.lastPrice).toFixed(2)}
  • `; + mobileList.innerHTML += `
  • ${base}${parseFloat(c.lastPrice).toLocaleString()}
  • `; }); } @@ -280,32 +349,90 @@ } function toggleMode(side, mode) { - const pInput = document.getElementById(side === 'contract' ? 'c-price' : side + '-price'); - if (mode === 'market') { - pInput.value = '市价价格'; - pInput.disabled = true; - pInput.style.color = '#f0b90b'; - } else { - pInput.value = currentPrice || ''; - pInput.disabled = false; - pInput.style.color = 'white'; + if (side === 'buy') { + const pInput = document.getElementById('buy-price'); + const label = document.getElementById('buy-label'); + const unit = document.getElementById('buy-unit'); + if (mode === 'market') { + pInput.value = '市价价格'; + pInput.disabled = true; + pInput.style.color = '#f0b90b'; + label.textContent = '成交额'; + unit.textContent = 'USDT'; + } else { + pInput.value = currentPrice || ''; + pInput.disabled = false; + pInput.style.color = 'white'; + label.textContent = '数量'; + unit.textContent = baseAsset; + } + } else if (side === 'sell') { + const pInput = document.getElementById('sell-price'); + if (mode === 'market') { + pInput.value = '市价价格'; + pInput.disabled = true; + pInput.style.color = '#f0b90b'; + } else { + pInput.value = currentPrice || ''; + pInput.disabled = false; + pInput.style.color = 'white'; + } + } else if (side === 'contract') { + const pInput = document.getElementById('c-price'); + if (mode === 'market') { + pInput.value = '市价价格'; + pInput.disabled = true; + pInput.style.color = '#f0b90b'; + } else { + pInput.value = currentPrice || ''; + pInput.disabled = false; + pInput.style.color = 'white'; + } } } function applySlider(side) { const val = document.getElementById(side + '-slider').value; if (side === 'buy') { - document.getElementById('buy-total').value = (balance * (val / 100)).toFixed(2); + const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market'; + if (mode === 'market') { + document.getElementById('buy-amount').value = (balance * (val / 100)).toFixed(2); + } else { + const price = parseFloat(document.getElementById('buy-price').value); + if (price > 0) document.getElementById('buy-amount').value = (balance * (val / 100) / price).toFixed(4); + } } else if (side === 'sell') { - // Simulated 1.5 BTC for testing - document.getElementById('sell-amount').value = (1.5 * (val / 100)).toFixed(4); + document.getElementById('sell-amount').value = (assetBalance * (val / 100)).toFixed(4); } else if (side === 'contract') { const margin = (balance * (val / 100)); - document.getElementById('c-margin').textContent = margin.toFixed(2); + document.getElementById('c-margin-display').textContent = margin.toFixed(2); document.getElementById('c-amount').value = Math.floor((margin * leverage) / 100); } } + function updateSlider(side) { + if (side === 'buy') { + const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market'; + const amount = parseFloat(document.getElementById('buy-amount').value) || 0; + let percent = 0; + if (mode === 'market') { + percent = (amount / balance) * 100; + } else { + const price = parseFloat(document.getElementById('buy-price').value) || 0; + percent = (amount * price / balance) * 100; + } + document.getElementById('buy-slider').value = Math.min(100, percent); + } else if (side === 'sell') { + const amount = parseFloat(document.getElementById('sell-amount').value) || 0; + document.getElementById('sell-slider').value = Math.min(100, (amount / assetBalance) * 100); + } else if (side === 'contract') { + const lots = parseFloat(document.getElementById('c-amount').value) || 0; + const margin = (100 * lots) / leverage; + document.getElementById('c-margin-display').textContent = margin.toFixed(2); + document.getElementById('c-slider').value = Math.min(100, (margin / balance) * 100); + } + } + function setLev(l) { leverage = l; document.getElementById('lev-btn').textContent = '杠杆: ' + l + 'x'; @@ -328,7 +455,6 @@ document.getElementById('header-change').className = 'fw-bold ' + (chg>=0?'text-success':'text-danger'); document.getElementById('header-volume').textContent = (parseFloat(d.quoteVolume)/1000000).toFixed(2) + 'M'; - // Order Book const askD = document.getElementById('asks'); const bidD = document.getElementById('bids'); askD.innerHTML = ''; bidD.innerHTML = ''; for (let i = 0; i < 10; i++) { @@ -336,21 +462,75 @@ bidD.innerHTML += `
    ${(currentPrice-(i+1)*0.1).toFixed(2)}${(Math.random()*1.2).toFixed(4)}
    `; } - // Sync limit inputs if empty if (tradeType === 'SPOT') { if (!document.getElementById('buy-price').value && !document.getElementById('buy-price').disabled) document.getElementById('buy-price').value = currentPrice; if (!document.getElementById('sell-price').value && !document.getElementById('sell-price').disabled) document.getElementById('sell-price').value = currentPrice; } else { if (!document.getElementById('c-price').value && !document.getElementById('c-price').disabled) document.getElementById('c-price').value = currentPrice; } + + document.querySelectorAll('.current-p-val').forEach(el => { el.textContent = currentPrice.toLocaleString(); }); + document.querySelectorAll('.upl-val').forEach(el => { + const entry = parseFloat(el.getAttribute('data-entry')); + const side = el.getAttribute('data-side'); + const lots = parseFloat(el.getAttribute('data-lots')); + let upl = 0; + if (side === 'LONG') upl = (currentPrice - entry) / entry * (lots * 100); + else upl = (entry - currentPrice) / entry * (lots * 100); + el.textContent = (upl >= 0 ? '+' : '') + upl.toFixed(2) + ' USDT'; + el.className = 'upl-val ' + (upl >= 0 ? 'text-success' : 'text-danger'); + }); + } catch(e) {} } - function submitOrder(side) { - alert('订单已提交,正在匹配中...'); + async function submitOrder(side) { + const mode = tradeType === 'SPOT' + ? (document.getElementById(side.toLowerCase() + '-limit').checked ? 'LIMIT' : 'MARKET') + : (document.getElementById('c-limit').checked ? 'LIMIT' : 'MARKET'); + + let price = 0; + let amount = 0; + + if (tradeType === 'SPOT') { + price = document.getElementById(side.toLowerCase() + '-price').value; + amount = document.getElementById(side.toLowerCase() + '-amount').value; + } else { + price = document.getElementById('c-price').value; + amount = document.getElementById('c-amount').value; + } + + try { + const r = await fetch('{% url "core:submit_order" %}', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' }, + body: JSON.stringify({ + symbol: symbol, side: side, trade_type: tradeType, order_type: mode, + price: price === '市价价格' ? null : price, amount: amount, leverage: leverage + }) + }); + const res = await r.json(); + if (res.status === 'success') { + alert('下单成功!'); + location.reload(); + } else { + alert('下单失败: ' + res.message); + } + } catch(e) { alert('提交出错'); } + } + + async function closePos(id) { + if (confirm('确定要平仓吗?')) { + const r = await fetch(`/api/close_position/${id}/`, { + method: 'POST', + headers: { 'X-CSRFToken': '{{ csrf_token }}' } + }); + const res = await r.json(); + if (res.status === 'success') location.reload(); + } } initTV(); getMarkets(); tick(); setInterval(tick, 2000); -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 3c7eeea..36bca48 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,8 +1,11 @@ from django.urls import path from . import views +app_name = 'core' + urlpatterns = [ path('', views.index, name='index'), + path('trade//', views.trade, name='trade'), path('spot/', views.trade, {'trade_type': 'spot'}, name='spot_trade'), path('contract/', views.trade, {'trade_type': 'contract'}, name='contract_trade'), path('markets/', views.market_center, name='market_center'), @@ -14,6 +17,10 @@ urlpatterns = [ path('register/', views.register_view, name='register'), path('api/market_data/', views.market_data, name='market_data'), path('api/submit_order/', views.submit_order, name='submit_order'), + path('api/close_position//', views.close_position, name='close_position'), + + # Dynamic Article/Placeholder Route + path('article//', views.placeholder_view, name='placeholder'), # Footer links path('help/', views.placeholder_view, {'title': '帮助中心'}, name='help_center'), diff --git a/core/views.py b/core/views.py index 270d68f..bb6bb3d 100644 --- a/core/views.py +++ b/core/views.py @@ -1,32 +1,49 @@ -from django.shortcuts import render, redirect +from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib.auth import login, authenticate from django.contrib.auth.forms import UserCreationForm, AuthenticationForm from django.http import JsonResponse from django.contrib.auth.models import User from django.contrib import messages -from .models import Account, Order, Transaction, Cryptocurrency, SiteSettings +from .models import Account, Order, Transaction, Cryptocurrency, SiteSettings, Asset, Position import random import decimal import json from django.views.decorators.csrf import csrf_exempt +from django.db import transaction def index(request): return render(request, 'core/index.html') def trade(request, trade_type='spot'): symbol = request.GET.get('symbol', 'BTCUSDT') - # Fetch all active cryptocurrencies for the sidebar + base_symbol = symbol.replace('USDT', '') cryptos = Cryptocurrency.objects.filter(is_active=True) + account = None + assets = {} + base_asset_balance = decimal.Decimal('0') + + if request.user.is_authenticated: + account, _ = Account.objects.get_or_create(user=request.user) + for asset in account.assets.all(): + assets[asset.currency] = asset + if asset.currency == base_symbol: + base_asset_balance = asset.balance + context = { 'symbol': symbol, + 'base_symbol': base_symbol, 'trade_type': trade_type.upper(), 'cryptos': cryptos, + 'account': account, + 'assets': assets, + 'base_asset_balance': base_asset_balance, } return render(request, 'core/trade.html', context) def market_center(request): - return render(request, 'core/index.html', {'market_only': True}) + cryptos = Cryptocurrency.objects.filter(is_active=True) + return render(request, 'core/market_center.html', {'cryptos': cryptos}) def placeholder_view(request, title): settings = SiteSettings.objects.first() @@ -61,10 +78,12 @@ def profile(request): account, created = Account.objects.get_or_create(user=request.user) recent_transactions = Transaction.objects.filter(account=account).order_by('-timestamp')[:10] recent_orders = Order.objects.filter(account=account).order_by('-created_at')[:10] + positions = Position.objects.filter(account=account, is_active=True) context = { 'account': account, 'recent_transactions': recent_transactions, 'recent_orders': recent_orders, + 'positions': positions, } return render(request, 'core/profile.html', context) @@ -82,7 +101,7 @@ def deposit(request): status='pending', tx_hash=tx_id ) - return redirect('profile') + return redirect('core:profile') return render(request, 'core/deposit.html') @login_required @@ -103,7 +122,7 @@ def withdraw(request): status='completed', tx_hash=f"wd_{random.randint(1000, 9999)}" ) - return redirect('profile') + return redirect('core:profile') return render(request, 'core/withdraw.html', {'account': account}) @login_required @@ -112,21 +131,9 @@ def verify(request): if request.method == 'POST': account.kyc_status = 'pending' account.save() - return redirect('profile') + return redirect('core:profile') return render(request, 'core/verify.html', {'account': account}) -def generate_captcha(): - a = random.randint(1, 10) - b = random.randint(1, 10) - op = random.choice(['+', '-', '*']) - if op == '+': - res = a + b - elif op == '-': - res = a - b - else: - res = a * b - return f"{a} {op} {b} = ?", res - def register_view(request): if request.method == 'POST': username = request.POST.get('username') @@ -145,9 +152,8 @@ def register_view(request): user = User.objects.create_user(username=username, password=password) Account.objects.get_or_create(user=user) login(request, user) - return redirect('index') + return redirect('core:index') - # Generate new captcha captcha_text, captcha_result = generate_captcha() request.session['captcha_result'] = captcha_result @@ -155,13 +161,22 @@ def register_view(request): 'captcha_text': captcha_text }) +def generate_captcha(): + a = random.randint(1, 10) + b = random.randint(1, 10) + op = random.choice(['+', '-', '*']) + if op == '+': res = a + b + elif op == '-': res = a - b + else: res = a * b + return f"{a} {op} {b} = ?", res + def login_view(request): if request.method == 'POST': form = AuthenticationForm(data=request.POST) if form.is_valid(): user = form.get_user() login(request, user) - return redirect('index') + return redirect('core:index') else: form = AuthenticationForm() return render(request, 'core/login.html', {'form': form}) @@ -169,76 +184,181 @@ def login_view(request): @login_required @csrf_exempt def submit_order(request): - if request.method == 'POST': - try: - data = json.loads(request.body) - symbol = data.get('symbol', 'BTCUSDT') - side = data.get('side') - amount = decimal.Decimal(data.get('amount', 0)) - trade_type = data.get('trade_type', 'SPOT') - order_type = data.get('order_type', 'MARKET') - price = data.get('price') + if request.method != 'POST': + return JsonResponse({'status': 'error', 'message': 'Invalid request'}) + + try: + data = json.loads(request.body) + symbol = data.get('symbol', 'BTCUSDT') + side = data.get('side') # BUY or SELL + trade_type = data.get('trade_type', 'SPOT') + order_type = data.get('order_type', 'MARKET') + price_val = data.get('price') + amount_val = data.get('amount', 0) + leverage = int(data.get('leverage', 20)) + + account = request.user.account + base_symbol = symbol.replace('USDT', '') + + # Get current market price for validations and market orders + crypto = Cryptocurrency.objects.filter(symbol=base_symbol).first() + current_price = crypto.current_price if (crypto and crypto.current_price > 0) else decimal.Decimal('48000') + + with transaction.atomic(): + if trade_type == 'SPOT': + if order_type == 'MARKET': + # Spot Market Order: Execute Immediately + if side == 'BUY': + # BUY: amount_val is USDT + total_usdt = decimal.Decimal(str(amount_val)) + if account.balance < total_usdt: + return JsonResponse({'status': 'error', 'message': '余额不足'}) + + exec_amount = total_usdt / current_price + account.balance -= total_usdt + account.save() + + asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol) + asset.balance += exec_amount + asset.save() + + Order.objects.create( + account=account, symbol=symbol, side=side, order_type=order_type, + trade_type=trade_type, amount=exec_amount, total_usdt=total_usdt, status='FILLED' + ) + else: # SELL + # SELL: amount_val is coin quantity + exec_amount = decimal.Decimal(str(amount_val)) + asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol) + if asset.balance < exec_amount: + return JsonResponse({'status': 'error', 'message': f'{base_symbol} 余额不足'}) + + total_usdt = exec_amount * current_price + asset.balance -= exec_amount + asset.save() + + account.balance += total_usdt + account.save() + + Order.objects.create( + account=account, symbol=symbol, side=side, order_type=order_type, + trade_type=trade_type, amount=exec_amount, total_usdt=total_usdt, status='FILLED' + ) + else: # LIMIT + # Spot Limit Order: Freeze Assets, Pend + price = decimal.Decimal(str(price_val)) + amount = decimal.Decimal(str(amount_val)) + if side == 'BUY': + total_usdt = price * amount + if account.balance < total_usdt: + return JsonResponse({'status': 'error', 'message': '余额不足'}) + + account.balance -= total_usdt + account.frozen_balance += total_usdt + account.save() + + Order.objects.create( + account=account, symbol=symbol, side=side, order_type=order_type, + trade_type=trade_type, amount=amount, price=price, total_usdt=total_usdt, status='PENDING' + ) + else: # SELL + asset, _ = Asset.objects.get_or_create(account=account, currency=base_symbol) + if asset.balance < amount: + return JsonResponse({'status': 'error', 'message': f'{base_symbol} 余额不足'}) + + asset.balance -= amount + asset.frozen += amount + asset.save() + + Order.objects.create( + account=account, symbol=symbol, side=side, order_type=order_type, + trade_type=trade_type, amount=amount, price=price, status='PENDING' + ) - account = request.user.account - - # Fetch current price from DB or simulated - crypto = Cryptocurrency.objects.filter(symbol=symbol.replace('USDT', '')).first() - current_price = crypto.current_price if crypto else decimal.Decimal('48000') - - # If market order, use current price. If limit, use provided price. - exec_price = decimal.Decimal(str(price)) if order_type == 'LIMIT' else current_price - - if side == 'BUY': - cost = amount * exec_price - if account.balance >= cost: - account.balance -= cost - account.save() - Order.objects.create( - account=account, - symbol=symbol, - side=side, - amount=amount, - trade_type=trade_type, - order_type=order_type, - status='FILLED', - entry_price=exec_price, - price=exec_price if order_type == 'LIMIT' else None - ) - return JsonResponse({'status': 'success'}) - else: - return JsonResponse({'status': 'error', 'message': '余额不足'}) - else: - # SELL - revenue = amount * exec_price - Order.objects.create( - account=account, - symbol=symbol, - side=side, - amount=amount, - trade_type=trade_type, - order_type=order_type, - status='FILLED', - entry_price=exec_price, - price=exec_price if order_type == 'LIMIT' else None - ) - account.balance += revenue - account.save() - return JsonResponse({'status': 'success'}) + else: # CONTRACT + # Contract: Initial Margin = face_value(100) * lots / leverage + lots = decimal.Decimal(str(amount_val)) + face_value = decimal.Decimal('100') + margin_required = (face_value * lots) / decimal.Decimal(str(leverage)) - except Exception as e: - return JsonResponse({'status': 'error', 'message': str(e)}) - return JsonResponse({'status': 'error', 'message': 'Invalid request'}) + if account.balance < margin_required: + return JsonResponse({'status': 'error', 'message': '保证金不足'}) + + if order_type == 'MARKET': + # Contract Market Order: Immediate Open Position + account.balance -= margin_required + account.save() + + Position.objects.create( + account=account, symbol=symbol, side='LONG' if side == 'BUY' else 'SHORT', + leverage=leverage, entry_price=current_price, lots=lots, margin=margin_required + ) + + Order.objects.create( + account=account, symbol=symbol, side=side, order_type=order_type, + trade_type=trade_type, amount=lots, leverage=leverage, status='FILLED' + ) + else: # LIMIT + # Contract Limit Order: Freeze Margin, Pend + price = decimal.Decimal(str(price_val)) + account.balance -= margin_required + account.frozen_balance += margin_required + account.save() + + Order.objects.create( + account=account, symbol=symbol, side=side, order_type=order_type, + trade_type=trade_type, amount=lots, price=price, leverage=leverage, status='PENDING' + ) + + return JsonResponse({'status': 'success'}) + + except Exception as e: + return JsonResponse({'status': 'error', 'message': str(e)}) + +@login_required +@csrf_exempt +def close_position(request, position_id): + if request.method != 'POST': + return JsonResponse({'status': 'error', 'message': 'Invalid request'}) + + position = get_object_or_404(Position, id=position_id, account=request.user.account, is_active=True) + + base_symbol = position.symbol.replace('USDT', '') + crypto = Cryptocurrency.objects.filter(symbol=base_symbol).first() + current_price = crypto.current_price if (crypto and crypto.current_price > 0) else decimal.Decimal('48000') + + face_value = decimal.Decimal('100') + # Calculate Unrealized P&L + if position.side == 'LONG': + upl = (current_price - position.entry_price) / position.entry_price * (position.lots * face_value) + else: + upl = (position.entry_price - current_price) / position.entry_price * (position.lots * face_value) + + with transaction.atomic(): + account = request.user.account + # Settlement: Margin + P&L - Fee (0.05%) + fee = (position.lots * face_value) * decimal.Decimal('0.0005') + account.balance += (position.margin + upl - fee) + account.save() + + position.is_active = False + position.save() + + # Log closing order + Order.objects.create( + account=account, symbol=position.symbol, side='SELL' if position.side == 'LONG' else 'BUY', + order_type='MARKET', trade_type='CONTRACT', amount=position.lots, status='FILLED' + ) + + return JsonResponse({'status': 'success'}) def market_data(request): - # This might be used by some local scripts, though index/trade use Binance API directly - symbols = ['BTC', 'ETH', 'BNB', 'SOL', 'ADA', 'XRP', 'DOT', 'DOGE'] + cryptos = Cryptocurrency.objects.filter(is_active=True) data = [] - for s in symbols: - price = random.uniform(10, 60000) - change = random.uniform(-5, 5) + for c in cryptos: data.append({ - 'symbol': s, - 'price': round(price, 4), - 'change': round(change, 2) + 'symbol': c.symbol, + 'price': float(c.current_price), + 'change': float(c.change_24h) }) return JsonResponse(data, safe=False) \ No newline at end of file